1import bpy, mathutils 2from . import util 3 4bl_info = { 5 'name': 'Curve Remove Doubles', 6 'author': 'Michael Soluyanov', 7 'version': (1, 1), 8 'blender': (2, 80, 0), 9 'location': 'View3D > Context menu (W/RMB) > Remove Doubles', 10 'description': 'Adds comand "Remove Doubles" for curves', 11 'category': 'Add Curve' 12} 13 14def main(context, distance = 0.01): 15 16 selected_Curves = util.GetSelectedCurves() 17 if bpy.ops.object.mode_set.poll(): 18 bpy.ops.object.mode_set(mode='EDIT') 19 20 for curve in selected_Curves: 21 bezier_dellist = [] 22 dellist = [] 23 24 for spline in curve.data.splines: 25 if spline.type == 'BEZIER': 26 if len(spline.bezier_points) > 1: 27 for i in range(0, len(spline.bezier_points)): 28 29 if i == 0: 30 ii = len(spline.bezier_points) - 1 31 else: 32 ii = i - 1 33 34 dot = spline.bezier_points[i]; 35 dot1 = spline.bezier_points[ii]; 36 37 while dot1 in bezier_dellist and i != ii: 38 ii -= 1 39 if ii < 0: 40 ii = len(spline.bezier_points)-1 41 dot1 = spline.bezier_points[ii] 42 43 if dot.select_control_point and dot1.select_control_point and (i!=0 or spline.use_cyclic_u): 44 45 if (dot.co-dot1.co).length < distance: 46 # remove points and recreate hangles 47 dot1.handle_right_type = "FREE" 48 dot1.handle_right = dot.handle_right 49 dot1.co = (dot.co + dot1.co) / 2 50 bezier_dellist.append(dot) 51 52 else: 53 # Handles that are on main point position converts to vector, 54 # if next handle are also vector 55 if dot.handle_left_type == 'VECTOR' and (dot1.handle_right - dot1.co).length < distance: 56 dot1.handle_right_type = "VECTOR" 57 if dot1.handle_right_type == 'VECTOR' and (dot.handle_left - dot.co).length < distance: 58 dot.handle_left_type = "VECTOR" 59 else: 60 if len(spline.points) > 1: 61 for i in range(0, len(spline.points)): 62 63 if i == 0: 64 ii = len(spline.points) - 1 65 else: 66 ii = i - 1 67 68 dot = spline.points[i]; 69 dot1 = spline.points[ii]; 70 71 while dot1 in dellist and i != ii: 72 ii -= 1 73 if ii < 0: 74 ii = len(spline.points)-1 75 dot1 = spline.points[ii] 76 77 if dot.select and dot1.select and (i!=0 or spline.use_cyclic_u): 78 79 if (dot.co-dot1.co).length < distance: 80 dot1.co = (dot.co + dot1.co) / 2 81 dellist.append(dot) 82 83 bpy.ops.curve.select_all(action = 'DESELECT') 84 85 for dot in bezier_dellist: 86 dot.select_control_point = True 87 88 for dot in dellist: 89 dot.select = True 90 91 bezier_count = len(bezier_dellist) 92 count = len(dellist) 93 94 bpy.ops.curve.delete(type = 'VERT') 95 96 bpy.ops.curve.select_all(action = 'DESELECT') 97 98 return bezier_count + count 99 100 101 102class CurveRemvDbs(bpy.types.Operator): 103 """Merge consecutive points that are near to each other""" 104 bl_idname = 'curvetools.remove_doubles' 105 bl_label = 'Remove Doubles' 106 bl_options = {'REGISTER', 'UNDO'} 107 108 distance: bpy.props.FloatProperty(name = 'Distance', default = 0.01) 109 110 @classmethod 111 def poll(cls, context): 112 return util.Selected1OrMoreCurves() 113 114 def execute(self, context): 115 removed=main(context, self.distance) 116 self.report({'INFO'}, "Removed %d bezier points" % removed) 117 return {'FINISHED'} 118 119 120 121def menu_func(self, context): 122 self.layout.operator(CurveRemvDbs.bl_idname, text='Remove Doubles') 123 124def register(): 125 bpy.utils.register_class(CurveRemvDbs) 126 bpy.types.VIEW3D_MT_edit_curve_context_menu.append(menu_func) 127 128def unregister(): 129 bpy.utils.unregister_class(CurveRemvDbs) 130 bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(menu_func) 131 132if __name__ == "__main__": 133 register() 134 135operators = [CurveRemvDbs] 136