1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # DevBo Task: https://developer.blender.org/T37377
8 "name": "Bevel/Taper Curve",
11 "blender": (2, 80, 0),
12 "location": "View3D > Object > Bevel/Taper",
13 "description": "Adds bevel and/or taper curve to active curve",
15 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
21 from bpy
.types
import (
25 from bpy
.props
import (
30 from bpy_extras
.object_utils
import (
36 def add_taper(self
, context
):
37 scale_ends1
= self
.scale_ends1
38 scale_ends2
= self
.scale_ends2
39 scale_mid
= self
.scale_mid
41 (-2.0, 1.0 * scale_ends1
, 0.0, 1.0),
42 (-1.0, 0.75 * scale_mid
, 0.0, 1.0),
43 (0.0, 1.5 * scale_mid
, 0.0, 1.0),
44 (1.0, 0.75 * scale_mid
, 0.0, 1.0),
45 (2.0, 1.0 * scale_ends2
, 0.0, 1.0)
47 make_path(self
, context
, verts
)
50 def add_type5(self
, context
):
51 scale_x
= self
.scale_x
52 scale_y
= self
.scale_y
54 [0.0 * scale_x
, 0.049549 * scale_y
,
55 0.0, 0.031603 * scale_x
, 0.047013 * scale_y
,
56 0.0, 0.05 * scale_x
, 0.0 * scale_y
, 0.0,
57 0.031603 * scale_x
, -0.047013 * scale_y
,
58 0.0, 0.0 * scale_x
, -0.049549 * scale_y
,
59 0.0, -0.031603 * scale_x
, -0.047013 * scale_y
,
60 0.0, -0.05 * scale_x
, -0.0 * scale_y
, 0.0,
61 -0.031603 * scale_x
, 0.047013 * scale_y
, 0.0]
64 [(-0.008804 * scale_x
, 0.049549 * scale_y
, 0.0),
65 (0.021304 * scale_x
, 0.02119 * scale_y
, 0.0),
66 (0.05 * scale_x
, 0.051228 * scale_y
, 0.0),
67 (0.036552 * scale_x
, -0.059423 * scale_y
, 0.0),
68 (0.008804 * scale_x
, -0.049549 * scale_y
, 0.0),
69 (-0.021304 * scale_x
, -0.02119 * scale_y
, 0.0),
70 (-0.05 * scale_x
, -0.051228 * scale_y
, 0.0),
71 (-0.036552 * scale_x
, 0.059423 * scale_y
, 0.0)]
74 [(0.008803 * scale_x
, 0.049549 * scale_y
, 0.0),
75 (0.036552 * scale_x
, 0.059423 * scale_y
, 0.0),
76 (0.05 * scale_x
, -0.051228 * scale_y
, 0.0),
77 (0.021304 * scale_x
, -0.02119 * scale_y
, 0.0),
78 (-0.008803 * scale_x
, -0.049549 * scale_y
, 0.0),
79 (-0.036552 * scale_x
, -0.059423 * scale_y
, 0.0),
80 (-0.05 * scale_x
, 0.051228 * scale_y
, 0.0),
81 (-0.021304 * scale_x
, 0.02119 * scale_y
, 0.0)]
83 make_curve(self
, context
, verts
, lhandles
, rhandles
)
86 def add_type4(self
, context
):
87 scale_x
= self
.scale_x
88 scale_y
= self
.scale_y
90 [-0.0 * scale_x
, 0.017183 * scale_y
,
91 0.0, 0.05 * scale_x
, 0.0 * scale_y
,
92 0.0, 0.0 * scale_x
, -0.017183 * scale_y
,
93 0.0, -0.05 * scale_x
, -0.0 * scale_y
, 0.0]
96 [(-0.017607 * scale_x
, 0.017183 * scale_y
, 0.0),
97 (0.05 * scale_x
, 0.102456 * scale_y
, 0.0),
98 (0.017607 * scale_x
, -0.017183 * scale_y
, 0.0),
99 (-0.05 * scale_x
, -0.102456 * scale_y
, 0.0)]
102 [(0.017607 * scale_x
, 0.017183 * scale_y
, 0.0),
103 (0.05 * scale_x
, -0.102456 * scale_y
, 0.0),
104 (-0.017607 * scale_x
, -0.017183 * scale_y
, 0.0),
105 (-0.05 * scale_x
, 0.102456 * scale_y
, 0.0)]
107 make_curve(self
, context
, verts
, lhandles
, rhandles
)
110 def add_type3(self
, context
):
111 scale_x
= self
.scale_x
112 scale_y
= self
.scale_y
114 [-0.017183 * scale_x
, 0.0 * scale_y
,
115 0.0, 0.0 * scale_x
, 0.05 * scale_y
,
116 0.0, 0.017183 * scale_x
, 0.0 * scale_y
,
117 0.0, 0.0 * scale_x
, -0.05 * scale_y
, 0.0]
120 [(-0.017183 * scale_x
, -0.017607 * scale_y
, 0.0),
121 (-0.102456 * scale_x
, 0.05 * scale_y
, 0.0),
122 (0.017183 * scale_x
, 0.017607 * scale_y
, 0.0),
123 (0.102456 * scale_x
, -0.05 * scale_y
, 0.0)]
126 [(-0.017183 * scale_x
, 0.017607 * scale_y
, 0.0),
127 (0.102456 * scale_x
, 0.05 * scale_y
, 0.0),
128 (0.017183 * scale_x
, -0.017607 * scale_y
, 0.0),
129 (-0.102456 * scale_x
, -0.05 * scale_y
, 0.0)]
131 make_curve(self
, context
, verts
, lhandles
, rhandles
)
134 def add_type2(self
, context
):
135 scale_x
= self
.scale_x
136 scale_y
= self
.scale_y
138 [-0.05 * scale_x
, 0.0 * scale_y
,
139 0.0, 0.0 * scale_x
, 0.05 * scale_y
,
140 0.0, 0.05 * scale_x
, 0.0 * scale_y
,
141 0.0, 0.0 * scale_x
, -0.05 * scale_y
, 0.0]
144 [(-0.05 * scale_x
, -0.047606 * scale_y
, 0.0),
145 (-0.047606 * scale_x
, 0.05 * scale_y
, 0.0),
146 (0.05 * scale_x
, 0.047607 * scale_y
, 0.0),
147 (0.047606 * scale_x
, -0.05 * scale_y
, 0.0)]
150 [(-0.05 * scale_x
, 0.047607 * scale_y
, 0.0),
151 (0.047607 * scale_x
, 0.05 * scale_y
, 0.0),
152 (0.05 * scale_x
, -0.047607 * scale_y
, 0.0),
153 (-0.047607 * scale_x
, -0.05 * scale_y
, 0.0)]
155 make_curve(self
, context
, verts
, lhandles
, rhandles
)
158 def add_type1(self
, context
):
159 scale_x
= self
.scale_x
160 scale_y
= self
.scale_y
162 [-0.05 * scale_x
, 0.0 * scale_y
,
163 0.0, 0.0 * scale_x
, 0.05 * scale_y
,
164 0.0, 0.05 * scale_x
, 0.0 * scale_y
,
165 0.0, 0.0 * scale_x
, -0.05 * scale_y
, 0.0]
168 [(-0.05 * scale_x
, -0.027606 * scale_y
, 0.0),
169 (-0.027606 * scale_x
, 0.05 * scale_y
, 0.0),
170 (0.05 * scale_x
, 0.027606 * scale_y
, 0.0),
171 (0.027606 * scale_x
, -0.05 * scale_y
, 0.0)]
174 [(-0.05 * scale_x
, 0.027607 * scale_y
, 0.0),
175 (0.027607 * scale_x
, 0.05 * scale_y
, 0.0),
176 (0.05 * scale_x
, -0.027607 * scale_y
, 0.0),
177 (-0.027607 * scale_x
, -0.05 * scale_y
, 0.0)]
179 make_curve(self
, context
, verts
, lhandles
, rhandles
)
182 def make_path(self
, context
, verts
):
183 target
= bpy
.context
.view_layer
.objects
.active
184 bpy
.ops
.curve
.primitive_nurbs_path_add(
185 align
='WORLD', enter_editmode
=False, location
=(0, 0, 0)
187 target
.data
.taper_object
= bpy
.context
.view_layer
.objects
.active
188 taper
= bpy
.context
.view_layer
.objects
.active
189 taper
.name
= target
.name
+ '_Taper'
190 bpy
.context
.view_layer
.objects
.active
= target
191 points
= taper
.data
.splines
[0].points
193 for i
in range(len(verts
)):
194 points
[i
].co
= verts
[i
]
197 def make_curve(self
, context
, verts
, lh
, rh
):
198 target
= bpy
.context
.view_layer
.objects
.active
199 curve_data
= bpy
.data
.curves
.new(
200 name
=target
.name
+ '_Bevel', type='CURVE'
202 curve_data
.dimensions
= '3D'
203 curve_data
.fill_mode
= 'FULL'
205 for p
in range(len(verts
)):
207 spline
= curve_data
.splines
.new(type='BEZIER')
208 spline
.use_cyclic_u
= True
209 spline
.bezier_points
.add(int(len(verts
[p
]) / 3 - 1))
210 spline
.bezier_points
.foreach_set('co', verts
[p
])
212 for bp
in spline
.bezier_points
:
213 bp
.handle_left_type
= 'ALIGNED'
214 bp
.handle_right_type
= 'ALIGNED'
215 bp
.handle_left
.xyz
= lh
[p
][c
]
216 bp
.handle_right
.xyz
= rh
[p
][c
]
219 object_data_add(context
, curve_data
, operator
=self
)
220 target
.data
.bevel_object
= bpy
.context
.view_layer
.objects
.active
221 bpy
.context
.view_layer
.objects
.active
= target
224 class add_tapercurve(Operator
):
225 bl_idname
= "curve.tapercurve"
226 bl_label
= "Add Curve as Taper"
227 bl_description
= ("Add taper curve to Active Curve\n"
228 "Needs an existing Active Curve")
229 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
231 scale_ends1
: FloatProperty(
232 name
="End Width Left",
233 description
="Adjust left end taper",
237 scale_ends2
: FloatProperty(
238 name
="End Width Right",
239 description
="Adjust right end taper",
243 scale_mid
: FloatProperty(
245 description
="Adjust taper at center",
249 link1
: BoolProperty(
251 description
="Link the End Width Left / Right settings\n"
252 "End Width Left will be editable ",
255 link2
: BoolProperty(
256 name
="Link Ends / Center",
257 description
="Link the End Widths with the Center Width",
260 diff
: FloatProperty(
263 description
="Difference between ends and center while linked"
265 edit_mode
: BoolProperty(
266 name
="Show in edit mode",
268 description
="Show in edit mode"
272 def poll(cls
, context
):
273 obj
= context
.active_object
274 return context
.mode
== 'OBJECT' and obj
and obj
.type == "CURVE"
276 def draw(self
, context
):
279 col
= layout
.column(align
=True)
280 col
.label(text
="Settings:")
281 split
= layout
.split(factor
=0.95, align
=True)
282 split
.active
= not self
.link2
283 col
= split
.column(align
=True)
284 col
.prop(self
, "scale_ends1")
286 row
= split
.row(align
=True)
288 col_sub
= col
.column(align
=True)
289 col_sub
.active
= not self
.link1
290 col_sub
.prop(self
, "scale_ends2")
291 row
.prop(self
, "link1", toggle
=True, text
="", icon
="LINKED")
293 split
= layout
.split(factor
=0.95, align
=True)
294 col
= split
.column(align
=True)
295 col
.prop(self
, "scale_mid")
297 row
= split
.row(align
=True)
299 col_sub
= col
.column(align
=True)
300 col_sub
.active
= self
.link2
301 row
.prop(self
, "link2", toggle
=True, text
="", icon
="LINKED")
302 col_sub
.prop(self
, "diff")
304 col
= layout
.column()
305 col
.row().prop(self
, "edit_mode", expand
=True)
307 def execute(self
, context
):
309 self
.scale_ends2
= self
.scale_ends1
312 self
.scale_ends2
= self
.scale_ends1
= self
.scale_mid
- self
.diff
314 add_taper(self
, context
)
317 bpy
.ops
.object.mode_set(mode
= 'EDIT')
319 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
324 class add_bevelcurve(Operator
, AddObjectHelper
):
325 bl_idname
= "curve.bevelcurve"
326 bl_label
= "Add Curve as Bevel"
327 bl_description
= ("Add bevel curve to Active Curve\n"
328 "Needs an existing Active Curve")
329 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
333 description
="Type of bevel curve",
337 scale_x
: FloatProperty(
339 description
="Scale on X axis",
342 scale_y
: FloatProperty(
344 description
="Scale on Y axis",
349 description
="Link the Scale on X/Y axis",
352 edit_mode
: BoolProperty(
353 name
="Show in edit mode",
355 description
="Show in edit mode"
359 def poll(cls
, context
):
360 obj
= context
.active_object
361 return context
.mode
== 'OBJECT' and obj
and obj
.type == "CURVE"
363 def draw(self
, context
):
366 col
= layout
.column(align
=True)
367 # AddObjectHelper props
368 col
.prop(self
, "align")
369 col
.prop(self
, "location")
370 col
.prop(self
, "rotation")
372 col
= layout
.column(align
=True)
373 col
.label(text
= "Settings:")
374 col
.prop(self
, "types")
376 split
= layout
.split(factor
=0.95, align
=True)
377 col
= split
.column(align
=True)
378 col
.prop(self
, "scale_x")
379 row
= split
.row(align
=True)
381 col
.prop(self
, "scale_y")
382 row
.prop(self
, "link", toggle
=True, text
="", icon
="LINKED")
384 col
= layout
.column()
385 col
.row().prop(self
, "edit_mode", expand
=True)
387 def execute(self
, context
):
389 self
.scale_y
= self
.scale_x
391 add_type1(self
, context
)
393 add_type2(self
, context
)
395 add_type3(self
, context
)
397 add_type4(self
, context
)
399 add_type5(self
, context
)
402 bpy
.ops
.object.mode_set(mode
= 'EDIT')
404 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
409 def menu_funcs(self
, context
):
410 bl_label
= "Bevel/Taper"
414 if context
.view_layer
.objects
.active
:
415 if context
.view_layer
.objects
.active
.type == "CURVE":
416 layout
.operator("curve.bevelcurve")
417 layout
.operator("curve.tapercurve")
427 from bpy
.utils
import register_class
431 bpy
.types
.VIEW3D_MT_object_context_menu
.prepend(menu_funcs
)
434 from bpy
.utils
import unregister_class
435 for cls
in reversed(classes
):
436 unregister_class(cls
)
438 bpy
.types
.VIEW3D_MT_object_context_menu
.remove(menu_funcs
)
440 if __name__
== "__main__":