1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
9 'name': 'Curve Remove Doubles',
10 'author': 'Michael Soluyanov',
12 'blender': (2, 80, 0),
13 'location': 'View3D > Context menu (W/RMB) > Remove Doubles',
14 'description': 'Adds command "Remove Doubles" for curves',
15 'category': 'Add Curve'
18 def main(context
, distance
= 0.01):
20 selected_Curves
= util
.GetSelectedCurves()
21 if bpy
.ops
.object.mode_set
.poll():
22 bpy
.ops
.object.mode_set(mode
='EDIT')
24 for curve
in selected_Curves
:
28 for spline
in curve
.data
.splines
:
29 if spline
.type == 'BEZIER':
30 if len(spline
.bezier_points
) > 1:
31 for i
in range(0, len(spline
.bezier_points
)):
34 ii
= len(spline
.bezier_points
) - 1
38 dot
= spline
.bezier_points
[i
];
39 dot1
= spline
.bezier_points
[ii
];
41 while dot1
in bezier_dellist
and i
!= ii
:
44 ii
= len(spline
.bezier_points
)-1
45 dot1
= spline
.bezier_points
[ii
]
47 if dot
.select_control_point
and dot1
.select_control_point
and (i
!=0 or spline
.use_cyclic_u
):
49 if (dot
.co
-dot1
.co
).length
< distance
:
50 # remove points and recreate hangles
51 dot1
.handle_right_type
= "FREE"
52 dot1
.handle_right
= dot
.handle_right
53 dot1
.co
= (dot
.co
+ dot1
.co
) / 2
54 bezier_dellist
.append(dot
)
57 # Handles that are on main point position converts to vector,
58 # if next handle are also vector
59 if dot
.handle_left_type
== 'VECTOR' and (dot1
.handle_right
- dot1
.co
).length
< distance
:
60 dot1
.handle_right_type
= "VECTOR"
61 if dot1
.handle_right_type
== 'VECTOR' and (dot
.handle_left
- dot
.co
).length
< distance
:
62 dot
.handle_left_type
= "VECTOR"
64 if len(spline
.points
) > 1:
65 for i
in range(0, len(spline
.points
)):
68 ii
= len(spline
.points
) - 1
72 dot
= spline
.points
[i
];
73 dot1
= spline
.points
[ii
];
75 while dot1
in dellist
and i
!= ii
:
78 ii
= len(spline
.points
)-1
79 dot1
= spline
.points
[ii
]
81 if dot
.select
and dot1
.select
and (i
!=0 or spline
.use_cyclic_u
):
83 if (dot
.co
-dot1
.co
).length
< distance
:
84 dot1
.co
= (dot
.co
+ dot1
.co
) / 2
87 bpy
.ops
.curve
.select_all(action
= 'DESELECT')
89 for dot
in bezier_dellist
:
90 dot
.select_control_point
= True
95 bezier_count
= len(bezier_dellist
)
98 bpy
.ops
.curve
.delete(type = 'VERT')
100 bpy
.ops
.curve
.select_all(action
= 'DESELECT')
102 return bezier_count
+ count
106 class CurveRemvDbs(bpy
.types
.Operator
):
107 """Merge consecutive points that are near to each other"""
108 bl_idname
= 'curvetools.remove_doubles'
109 bl_label
= 'Remove Doubles'
110 bl_options
= {'REGISTER', 'UNDO'}
112 distance
: bpy
.props
.FloatProperty(name
= 'Distance', default
= 0.01)
115 def poll(cls
, context
):
116 return util
.Selected1OrMoreCurves()
118 def execute(self
, context
):
119 removed
=main(context
, self
.distance
)
120 self
.report({'INFO'}, "Removed %d bezier points" % removed
)
125 def menu_func(self
, context
):
126 self
.layout
.operator(CurveRemvDbs
.bl_idname
, text
='Remove Doubles')
129 bpy
.utils
.register_class(CurveRemvDbs
)
130 bpy
.types
.VIEW3D_MT_edit_curve_context_menu
.append(menu_func
)
133 bpy
.utils
.unregister_class(CurveRemvDbs
)
134 bpy
.types
.VIEW3D_MT_edit_curve_context_menu
.remove(menu_func
)
136 if __name__
== "__main__":
139 operators
= [CurveRemvDbs
]