diff --git a/OP_copy_paste.py b/OP_copy_paste.py index 08f43cf..726aa8c 100644 --- a/OP_copy_paste.py +++ b/OP_copy_paste.py @@ -45,105 +45,112 @@ def convertAttr(Attr): else: return(Attr) -def getMatrix (layer) : +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 - bone = object.pose.bones[layer.parent_bone] - matrix = bone.matrix @ object.matrix_world - matrix = matrix.copy() @ layer.matrix_inverse - else : - matrix = layer.parent.matrix_world @ layer.matrix_inverse + if layer.parent: + ## temp solution + matrix = layer.parent.matrix_world @ layer.matrix_inverse + + ### if layer.parent_type == 'BONE': + # if layer.parent.type == 'ARMATURE': + # object = layer.parent + # bone = object.pose.bones[layer.parent_bone] + # matrix = bone.matrix @ object.matrix_world + # matrix = matrix.copy() @ layer.matrix_inverse + # else : + # matrix = layer.parent.matrix_world @ layer.matrix_inverse return matrix.copy() -default_pt_uv_fill = Vector((0.5, 0.5)) +# default_pt_uv_fill = Vector((0.5, 0.5)) def dump_gp_point(p, l, obj, - pressure=True, strength=True, vertex_color=True, uv_fill=True, uv_factor=True, uv_rotation=True): + radius=True, opacity=True, vertex_color=True, fill_color=True, uv_factor=True, rotation=True): '''add properties of a given points to a dic and return it''' - pdic = {} - #point_attr_list = ('co', 'pressure', 'select', 'strength') #select#'rna_type' + point_dict = {} + #point_attr_list = ('co', 'radius', 'select', 'opacity') #select#'rna_type' #for att in point_attr_list: - # pdic[att] = convertAttr(getattr(p, att)) + # point_dict[att] = convertAttr(getattr(p, att)) if l.parent: mat = getMatrix(l) - pdic['co'] = convertAttr(obj.matrix_world @ mat @ getattr(p,'co')) + point_dict['position'] = convertAttr(obj.matrix_world @ mat @ getattr(p,'position')) else: - pdic['co'] = convertAttr(obj.matrix_world @ getattr(p,'co')) + point_dict['position'] = convertAttr(obj.matrix_world @ getattr(p,'position')) - # pdic['select'] = convertAttr(getattr(p,'select')) # need selection ? - if pressure and p.pressure != 1.0: - pdic['pressure'] = convertAttr(getattr(p,'pressure')) - if strength and p.strength != 1.0: - pdic['strength'] = convertAttr(getattr(p,'strength')) + # point_dict['select'] = convertAttr(getattr(p,'select')) # need selection ? + if radius and p.radius != 1.0: + point_dict['radius'] = convertAttr(getattr(p,'radius')) + + if opacity and p.opacity != 1.0: + point_dict['opacity'] = convertAttr(getattr(p,'opacity')) ## get vertex color (long...) if vertex_color and p.vertex_color[:] != (0.0, 0.0, 0.0, 0.0): - pdic['vertex_color'] = convertAttr(p.vertex_color) + point_dict['vertex_color'] = convertAttr(p.vertex_color) - ## UV attr (maybe uv fill is always (0.5,0.5) ? also exists at stroke level...) - if uv_fill and p.uv_fill != default_pt_uv_fill: - pdic['uv_fill'] = convertAttr(p.uv_fill) - if uv_factor and p.uv_factor != 0.0: - pdic['uv_factor'] = convertAttr(p.uv_factor) - if uv_rotation and p.uv_rotation != 0.0: - pdic['uv_rotation'] = convertAttr(p.uv_rotation) + if rotation and p.rotation != 0.0: + point_dict['rotation'] = convertAttr(p.rotation) - return pdic + ## No time infos + # if delta_time and p.delta_time != 0.0: + # point_dict['delta_time'] = convertAttr(getattr(p,'delta_time')) + return point_dict def dump_gp_stroke_range(s, sid, l, obj, - pressure=True, strength=True, vertex_color=True, uv_fill=True, uv_factor=True, uv_rotation=True): + radius=True, opacity=True, vertex_color=True, fill_color=True, fill_opacity=True, rotation=True): '''Get a grease pencil stroke and return a dic with attribute (points attribute being a dic of dics to store points and their attributes) ''' - sdic = {} - stroke_attr_list = ('line_width',) #'select'#read-only: 'triangles' - for att in stroke_attr_list: - sdic[att] = getattr(s, att) - + stroke_dict = {} + # stroke_attr_list = ('line_width',) + # for att in stroke_attr_list: + # stroke_dict[att] = getattr(s, att) + ## Dump following these value only if they are non default if s.material_index != 0: - sdic['material_index'] = s.material_index - - if getattr(s, 'draw_cyclic', None): # pre-2.92 - sdic['draw_cyclic'] = s.draw_cyclic - - if getattr(s, 'use_cyclic', None): # from 2.92 - sdic['use_cyclic'] = s.use_cyclic - - if s.uv_scale != 1.0: - sdic['uv_scale'] = s.uv_scale - - if s.uv_rotation != 0.0: - sdic['uv_rotation'] = s.uv_rotation - - if s.hardness != 1.0: - sdic['hardness'] = s.hardness - - if s.uv_translation != Vector((0.0, 0.0)): - sdic['uv_translation'] = convertAttr(s.uv_translation) + stroke_dict['material_index'] = s.material_index - if s.vertex_color_fill[:] != (0,0,0,0): - sdic['vertex_color_fill'] = convertAttr(s.vertex_color_fill) + if s.cyclic: + stroke_dict['cyclic'] = s.cyclic + + if s.softness != 0.0: + stroke_dict['softness'] = s.softness + + if s.aspect_ratio != 1.0: + stroke_dict['aspect_ratio'] = s.aspect_ratio + + if s.start_cap != 0: + stroke_dict['start_cap'] = s.start_cap + + if s.end_cap != 0: + stroke_dict['end_cap'] = s.end_cap + + if fill_color and s.fill_color[:] != (0,0,0,0): + stroke_dict['fill_color'] = convertAttr(s.fill_color) + + if fill_opacity and s.fill_opacity != 0.0: + stroke_dict['fill_opacity'] = s.fill_opacity + + ## No time infos + # if s.time_start != 0.0: + # stroke_dict['time_start'] = s.time_start points = [] if sid is None: # no ids, just full points... for p in s.points: - points.append(dump_gp_point(p,l,obj, - pressure=pressure, strength=strength, vertex_color=vertex_color, uv_fill=uv_fill, uv_factor=uv_factor, uv_rotation=uv_rotation)) + points.append(dump_gp_point(p, l, obj, + radius=radius, opacity=opacity, vertex_color=vertex_color, rotation=rotation)) else: for pid in sid: - points.append(dump_gp_point(s.points[pid],l,obj, - pressure=pressure, strength=strength, vertex_color=vertex_color, uv_fill=uv_fill, uv_factor=uv_factor, uv_rotation=uv_rotation)) - sdic['points'] = points - return sdic - + points.append(dump_gp_point(s.points[pid], l, obj, + radius=radius, opacity=opacity, vertex_color=vertex_color, rotation=rotation)) + + stroke_dict['points'] = points + return stroke_dict def copycut_strokes(layers=None, copy=True, keep_empty=True): @@ -177,7 +184,7 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True): staylist = [] # init part of strokes that must survive on this layer rm_list = [] # init strokes that must be removed from this layer - for s_index, stroke in f.drawing.strokes: + for s_index, stroke in enumerate(f.drawing.strokes): if stroke.select: # separate in multiple stroke if parts of the strokes a selected. sel = [i for i, p in enumerate(stroke.points) if p.select] @@ -196,8 +203,8 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True): if not copy: maxindex = len(stroke.points)-1 if len(substrokes) == maxindex+1: # if only one substroke, then it's the full stroke - # f.drawing.strokes.remove(stroke) - rm_list.append(stroke) + # f.drawing.strokes.remove(stroke) # gpv2 + rm_list.append(s_index) else: neg = [i for i, p in enumerate(stroke.points) if not p.select] @@ -216,21 +223,15 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True): staylist.append(dump_gp_stroke_range(stroke, ns, l, obj)) # make a negative list containing all last index - - '''#full stroke version - # 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(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... - f.drawing.strokes.remove_strokes(indices=selected_ids) + if selected_ids: + f.drawing.remove_strokes(indices=selected_ids) # ...recreate these uncutted ones #pprint(staylist) @@ -243,7 +244,6 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True): l.frames.remove(f) - print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds') #print(stroke_list) return stroke_list @@ -289,7 +289,7 @@ def copy_all_strokes(layers=None): """ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None, - pressure=True, strength=True, vertex_color=True, uv_fill=True, uv_factor=True, uv_rotation=True): + radius=True, opacity=True, vertex_color=True, fill_color=True, fill_opacity=True, rotation=True): ''' copy all stroke, not affected by selection on active frame layers can be None, a single layer object or list of layer object as filter @@ -323,7 +323,7 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None, # if s.select: # send index of all points to get the whole stroke with "range" stroke_list.append( dump_gp_stroke_range(s, [i for i in range(len(s.points))], l, obj, - pressure=pressure,strength=strength,vertex_color=vertex_color,uv_fill=uv_fill,uv_factor=uv_factor,uv_rotation=uv_rotation)) + radius=radius, opacity=opacity, vertex_color=vertex_color, fill_color=fill_color, fill_opacity=fill_opacity, rotation=rotation)) # print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds') return stroke_list @@ -331,26 +331,27 @@ 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.drawing.strokes.new() + pts_to_add = len(s['points']) + frame.drawing.add_strokes([pts_to_add]) + + ns = frame.drawing.strokes[-1] for att, val in s.items(): if att not in ('points'): setattr(ns, att, val) - pts_to_add = len(s['points']) - # print(pts_to_add, 'points')#dbg - ns.points.add(pts_to_add) + # ns.points.add(pts_to_add) ob_mat_inv = obj.matrix_world.inverted() - ## patch pressure 1 - # pressure_flat_list = [di['pressure'] for di in s['points']] #get all pressure flatened + ## patch radius 1 + # radius_flat_list = [di['radius'] for di in s['points']] #get all radius flatened - if layer.is_parented: + if layer.parent: mat = getMatrix(layer).inverted() for i, pt in enumerate(s['points']): for k, v in pt.items(): - if k == 'co': + if k == 'position': setattr(ns.points[i], k, v) ns.points[i].position = ob_mat_inv @ mat @ ns.points[i].position # invert of object * invert of layer * coordinate else: @@ -361,19 +362,14 @@ def add_stroke(s, frame, layer, obj, select=False): else: for i, pt in enumerate(s['points']): for k, v in pt.items(): - if k == 'co': + if k == 'position': setattr(ns.points[i], k, v) ns.points[i].position = ob_mat_inv @ ns.points[i].position# invert of object * coordinate else: setattr(ns.points[i], k, v) if select: ns.points[i].select = True - - ## trigger updapte (in 2.93 fix some drawing problem with fills and UVs) - ns.points.update() - ## patch pressure 2 - # ns.points.foreach_set('pressure', pressure_flat_list) def add_multiple_strokes(stroke_list, layer=None, use_current_frame=True, select=False): ''' @@ -436,7 +432,7 @@ class GPCLIP_OT_copy_strokes(bpy.types.Operator): # return {"CANCELLED"} t0 = time() - #ct = check_pressure() + #ct = check_radius() strokelist = copycut_strokes(copy=True, keep_empty=True) if not strokelist: self.report({'ERROR'}, 'Nothing to copy') @@ -465,7 +461,7 @@ class GPCLIP_OT_cut_strokes(bpy.types.Operator): # return {"CANCELLED"} t0 = time() - strokelist = copycut_strokes(copy=False, keep_empty=True) # ct = check_pressure() + strokelist = copycut_strokes(copy=False, keep_empty=True) # ct = check_radius() if not strokelist: self.report({'ERROR'},'Nothing to cut') return {"CANCELLED"} @@ -517,18 +513,20 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): def poll(cls, context): 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)') - strength : bpy.props.BoolProperty(name='strength', default=True, - description='Dump point strength attribute (already skipped if at default value)') + radius : bpy.props.BoolProperty(name='radius', default=True, + description='Dump point radius attribute (already skipped if at default value)') + opacity : bpy.props.BoolProperty(name='opacity', default=True, + description='Dump point opacity attribute (already skipped if at default value)') vertex_color : bpy.props.BoolProperty(name='vertex color', default=True, description='Dump point vertex_color attribute (already skipped if at default value)') - uv_fill : bpy.props.BoolProperty(name='uv fill', default=True, - description='Dump point uv_fill attribute (already skipped if at default value)') + fill_color : bpy.props.BoolProperty(name='fill color', default=True, + description='Dump point fill_color attribute (already skipped if at default value)') + fill_opacity : bpy.props.BoolProperty(name='fill opacity', default=True, + description='Dump point fill_opacity attribute (already skipped if at default value)') uv_factor : bpy.props.BoolProperty(name='uv factor', default=True, description='Dump point uv_factor attribute (already skipped if at default value)') - uv_rotation : bpy.props.BoolProperty(name='uv rotation', default=True, - description='Dump point uv_rotation attribute (already skipped if at default value)') + rotation : bpy.props.BoolProperty(name='rotation', default=True, + description='Dump point rotation attribute (already skipped if at default value)') def invoke(self, context, event): # self.file_dump = event.ctrl @@ -540,12 +538,12 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): layout.use_property_split = True col = layout.column() col.label(text='Keep following point attributes:') - col.prop(self, 'pressure') - col.prop(self, 'strength') + col.prop(self, 'radius') + col.prop(self, 'opacity') col.prop(self, 'vertex_color') - col.prop(self, 'uv_fill') - col.prop(self, 'uv_factor') - col.prop(self, 'uv_rotation') + col.prop(self, 'fill_color') + col.prop(self, 'fill_opacity') + col.prop(self, 'rotation') return def execute(self, context): @@ -556,7 +554,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): obj = context.object gpl = obj.data.layers t0 = time() - #ct = check_pressure() + #ct = check_radius() layerdic = {} layerpool = [l for l in gpl if not l.hide and l.select] # and not l.lock @@ -575,8 +573,8 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): 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, - pressure=self.pressure, strength=self.strength, vertex_color=self.vertex_color, - uv_fill=self.uv_fill, uv_factor=self.uv_factor, uv_rotation=self.uv_rotation) + radius=self.radius, opacity=self.opacity, vertex_color=self.vertex_color, + fill_color=self.fill_color, uv_factor=self.uv_factor, rotation=self.rotation) frame_dic[f.frame_number] = strokelist @@ -612,8 +610,8 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): continue strokelist = copy_all_strokes_in_frame(frame=f, layers=l, obj=obj, - pressure=self.pressure, strength=self.strength, vertex_color=self.vertex_color, - uv_fill=self.uv_fill, uv_factor=self.uv_factor, uv_rotation=self.uv_rotation) + radius=self.radius, opacity=self.opacity, vertex_color=self.vertex_color, + fill_color=self.fill_color, uv_factor=self.uv_factor, rotation=self.rotation) frame_dic[i] = strokelist diff --git a/OP_keyframe_jump.py b/OP_keyframe_jump.py index 7d1484e..6c9c564 100644 --- a/OP_keyframe_jump.py +++ b/OP_keyframe_jump.py @@ -36,6 +36,7 @@ class GPTB_OT_jump_gp_keyframe(bpy.types.Operator): ('MOVING_HOLD', 'Moving Hold', '', 'KEYTYPE_MOVING_HOLD_VEC', 4), ('EXTREME', 'Extreme', '', 'KEYTYPE_EXTREME_VEC', 5), ('JITTER', 'Jitter', '', 'KEYTYPE_JITTER_VEC', 6), + ('GENERATED', 'Generated', '', 'KEYTYPE_GENERATED_VEC', 7), )) def execute(self, context):