1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 # ##### END GPL LICENSE BLOCK #####
18 # DevBo Task: https://developer.blender.org/T37377
21 "name": "Bevel/Taper Curve",
24 "blender": (2, 80, 0),
25 "location": "View3D > Object > Bevel/Taper",
26 "description": "Adds bevel and/or taper curve to active curve",
28 "doc_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
29 "Py/Scripts/Curve/Bevel_-Taper_Curve",
35 from bpy
.types
import (
39 from bpy
.props
import (
44 from bpy_extras
.object_utils
import (
50 def add_taper(self
, context
):
51 scale_ends1
= self
.scale_ends1
52 scale_ends2
= self
.scale_ends2
53 scale_mid
= self
.scale_mid
55 (-2.0, 1.0 * scale_ends1
, 0.0, 1.0),
56 (-1.0, 0.75 * scale_mid
, 0.0, 1.0),
57 (0.0, 1.5 * scale_mid
, 0.0, 1.0),
58 (1.0, 0.75 * scale_mid
, 0.0, 1.0),
59 (2.0, 1.0 * scale_ends2
, 0.0, 1.0)
61 make_path(self
, context
, verts
)
64 def add_type5(self
, context
):
65 scale_x
= self
.scale_x
66 scale_y
= self
.scale_y
68 [0.0 * scale_x
, 0.049549 * scale_y
,
69 0.0, 0.031603 * scale_x
, 0.047013 * scale_y
,
70 0.0, 0.05 * scale_x
, 0.0 * scale_y
, 0.0,
71 0.031603 * scale_x
, -0.047013 * scale_y
,
72 0.0, 0.0 * scale_x
, -0.049549 * scale_y
,
73 0.0, -0.031603 * scale_x
, -0.047013 * scale_y
,
74 0.0, -0.05 * scale_x
, -0.0 * scale_y
, 0.0,
75 -0.031603 * scale_x
, 0.047013 * scale_y
, 0.0]
78 [(-0.008804 * scale_x
, 0.049549 * scale_y
, 0.0),
79 (0.021304 * scale_x
, 0.02119 * scale_y
, 0.0),
80 (0.05 * scale_x
, 0.051228 * scale_y
, 0.0),
81 (0.036552 * scale_x
, -0.059423 * scale_y
, 0.0),
82 (0.008804 * scale_x
, -0.049549 * scale_y
, 0.0),
83 (-0.021304 * scale_x
, -0.02119 * scale_y
, 0.0),
84 (-0.05 * scale_x
, -0.051228 * scale_y
, 0.0),
85 (-0.036552 * scale_x
, 0.059423 * scale_y
, 0.0)]
88 [(0.008803 * scale_x
, 0.049549 * scale_y
, 0.0),
89 (0.036552 * scale_x
, 0.059423 * scale_y
, 0.0),
90 (0.05 * scale_x
, -0.051228 * scale_y
, 0.0),
91 (0.021304 * scale_x
, -0.02119 * scale_y
, 0.0),
92 (-0.008803 * scale_x
, -0.049549 * scale_y
, 0.0),
93 (-0.036552 * scale_x
, -0.059423 * scale_y
, 0.0),
94 (-0.05 * scale_x
, 0.051228 * scale_y
, 0.0),
95 (-0.021304 * scale_x
, 0.02119 * scale_y
, 0.0)]
97 make_curve(self
, context
, verts
, lhandles
, rhandles
)
100 def add_type4(self
, context
):
101 scale_x
= self
.scale_x
102 scale_y
= self
.scale_y
104 [-0.0 * scale_x
, 0.017183 * scale_y
,
105 0.0, 0.05 * scale_x
, 0.0 * scale_y
,
106 0.0, 0.0 * scale_x
, -0.017183 * scale_y
,
107 0.0, -0.05 * scale_x
, -0.0 * scale_y
, 0.0]
110 [(-0.017607 * scale_x
, 0.017183 * scale_y
, 0.0),
111 (0.05 * scale_x
, 0.102456 * scale_y
, 0.0),
112 (0.017607 * scale_x
, -0.017183 * scale_y
, 0.0),
113 (-0.05 * scale_x
, -0.102456 * scale_y
, 0.0)]
116 [(0.017607 * scale_x
, 0.017183 * scale_y
, 0.0),
117 (0.05 * scale_x
, -0.102456 * scale_y
, 0.0),
118 (-0.017607 * scale_x
, -0.017183 * scale_y
, 0.0),
119 (-0.05 * scale_x
, 0.102456 * scale_y
, 0.0)]
121 make_curve(self
, context
, verts
, lhandles
, rhandles
)
124 def add_type3(self
, context
):
125 scale_x
= self
.scale_x
126 scale_y
= self
.scale_y
128 [-0.017183 * scale_x
, 0.0 * scale_y
,
129 0.0, 0.0 * scale_x
, 0.05 * scale_y
,
130 0.0, 0.017183 * scale_x
, 0.0 * scale_y
,
131 0.0, 0.0 * scale_x
, -0.05 * scale_y
, 0.0]
134 [(-0.017183 * scale_x
, -0.017607 * scale_y
, 0.0),
135 (-0.102456 * scale_x
, 0.05 * scale_y
, 0.0),
136 (0.017183 * scale_x
, 0.017607 * scale_y
, 0.0),
137 (0.102456 * scale_x
, -0.05 * scale_y
, 0.0)]
140 [(-0.017183 * scale_x
, 0.017607 * scale_y
, 0.0),
141 (0.102456 * scale_x
, 0.05 * scale_y
, 0.0),
142 (0.017183 * scale_x
, -0.017607 * scale_y
, 0.0),
143 (-0.102456 * scale_x
, -0.05 * scale_y
, 0.0)]
145 make_curve(self
, context
, verts
, lhandles
, rhandles
)
148 def add_type2(self
, context
):
149 scale_x
= self
.scale_x
150 scale_y
= self
.scale_y
152 [-0.05 * scale_x
, 0.0 * scale_y
,
153 0.0, 0.0 * scale_x
, 0.05 * scale_y
,
154 0.0, 0.05 * scale_x
, 0.0 * scale_y
,
155 0.0, 0.0 * scale_x
, -0.05 * scale_y
, 0.0]
158 [(-0.05 * scale_x
, -0.047606 * scale_y
, 0.0),
159 (-0.047606 * scale_x
, 0.05 * scale_y
, 0.0),
160 (0.05 * scale_x
, 0.047607 * scale_y
, 0.0),
161 (0.047606 * scale_x
, -0.05 * scale_y
, 0.0)]
164 [(-0.05 * scale_x
, 0.047607 * scale_y
, 0.0),
165 (0.047607 * scale_x
, 0.05 * scale_y
, 0.0),
166 (0.05 * scale_x
, -0.047607 * scale_y
, 0.0),
167 (-0.047607 * scale_x
, -0.05 * scale_y
, 0.0)]
169 make_curve(self
, context
, verts
, lhandles
, rhandles
)
172 def add_type1(self
, context
):
173 scale_x
= self
.scale_x
174 scale_y
= self
.scale_y
176 [-0.05 * scale_x
, 0.0 * scale_y
,
177 0.0, 0.0 * scale_x
, 0.05 * scale_y
,
178 0.0, 0.05 * scale_x
, 0.0 * scale_y
,
179 0.0, 0.0 * scale_x
, -0.05 * scale_y
, 0.0]
182 [(-0.05 * scale_x
, -0.027606 * scale_y
, 0.0),
183 (-0.027606 * scale_x
, 0.05 * scale_y
, 0.0),
184 (0.05 * scale_x
, 0.027606 * scale_y
, 0.0),
185 (0.027606 * scale_x
, -0.05 * scale_y
, 0.0)]
188 [(-0.05 * scale_x
, 0.027607 * scale_y
, 0.0),
189 (0.027607 * scale_x
, 0.05 * scale_y
, 0.0),
190 (0.05 * scale_x
, -0.027607 * scale_y
, 0.0),
191 (-0.027607 * scale_x
, -0.05 * scale_y
, 0.0)]
193 make_curve(self
, context
, verts
, lhandles
, rhandles
)
196 def make_path(self
, context
, verts
):
197 target
= bpy
.context
.view_layer
.objects
.active
198 bpy
.ops
.curve
.primitive_nurbs_path_add(
199 align
='WORLD', enter_editmode
=False, location
=(0, 0, 0)
201 target
.data
.taper_object
= bpy
.context
.view_layer
.objects
.active
202 taper
= bpy
.context
.view_layer
.objects
.active
203 taper
.name
= target
.name
+ '_Taper'
204 bpy
.context
.view_layer
.objects
.active
= target
205 points
= taper
.data
.splines
[0].points
207 for i
in range(len(verts
)):
208 points
[i
].co
= verts
[i
]
211 def make_curve(self
, context
, verts
, lh
, rh
):
212 target
= bpy
.context
.view_layer
.objects
.active
213 curve_data
= bpy
.data
.curves
.new(
214 name
=target
.name
+ '_Bevel', type='CURVE'
216 curve_data
.dimensions
= '3D'
217 curve_data
.fill_mode
= 'FULL'
219 for p
in range(len(verts
)):
221 spline
= curve_data
.splines
.new(type='BEZIER')
222 spline
.use_cyclic_u
= True
223 spline
.bezier_points
.add(len(verts
[p
]) / 3 - 1)
224 spline
.bezier_points
.foreach_set('co', verts
[p
])
226 for bp
in spline
.bezier_points
:
227 bp
.handle_left_type
= 'ALIGNED'
228 bp
.handle_right_type
= 'ALIGNED'
229 bp
.handle_left
.xyz
= lh
[p
][c
]
230 bp
.handle_right
.xyz
= rh
[p
][c
]
233 object_data_add(context
, curve_data
, operator
=self
)
234 target
.data
.bevel_object
= bpy
.context
.view_layer
.objects
.active
235 bpy
.context
.view_layer
.objects
.active
= target
238 class add_tapercurve(Operator
):
239 bl_idname
= "curve.tapercurve"
240 bl_label
= "Add Curve as Taper"
241 bl_description
= ("Add taper curve to Active Curve\n"
242 "Needs an existing Active Curve")
243 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
245 scale_ends1
: FloatProperty(
246 name
="End Width Left",
247 description
="Adjust left end taper",
251 scale_ends2
: FloatProperty(
252 name
="End Width Right",
253 description
="Adjust right end taper",
257 scale_mid
: FloatProperty(
259 description
="Adjust taper at center",
263 link1
: BoolProperty(
265 description
="Link the End Width Left / Right settings\n"
266 "End Width Left will be editable ",
269 link2
: BoolProperty(
270 name
="Link Ends / Center",
271 description
="Link the End Widths with the Center Width",
274 diff
: FloatProperty(
277 description
="Difference between ends and center while linked"
279 edit_mode
: BoolProperty(
280 name
="Show in edit mode",
282 description
="Show in edit mode"
286 def poll(cls
, context
):
287 obj
= context
.active_object
288 return context
.mode
== 'OBJECT' and obj
and obj
.type == "CURVE"
290 def draw(self
, context
):
293 col
= layout
.column(align
=True)
294 col
.label(text
="Settings:")
295 split
= layout
.split(factor
=0.95, align
=True)
296 split
.active
= not self
.link2
297 col
= split
.column(align
=True)
298 col
.prop(self
, "scale_ends1")
300 row
= split
.row(align
=True)
302 col_sub
= col
.column(align
=True)
303 col_sub
.active
= not self
.link1
304 col_sub
.prop(self
, "scale_ends2")
305 row
.prop(self
, "link1", toggle
=True, text
="", icon
="LINKED")
307 split
= layout
.split(factor
=0.95, align
=True)
308 col
= split
.column(align
=True)
309 col
.prop(self
, "scale_mid")
311 row
= split
.row(align
=True)
313 col_sub
= col
.column(align
=True)
314 col_sub
.active
= self
.link2
315 row
.prop(self
, "link2", toggle
=True, text
="", icon
="LINKED")
316 col_sub
.prop(self
, "diff")
318 col
= layout
.column()
319 col
.row().prop(self
, "edit_mode", expand
=True)
321 def execute(self
, context
):
323 self
.scale_ends2
= self
.scale_ends1
326 self
.scale_ends2
= self
.scale_ends1
= self
.scale_mid
- self
.diff
328 add_taper(self
, context
)
331 bpy
.ops
.object.mode_set(mode
= 'EDIT')
333 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
338 class add_bevelcurve(Operator
, AddObjectHelper
):
339 bl_idname
= "curve.bevelcurve"
340 bl_label
= "Add Curve as Bevel"
341 bl_description
= ("Add bevel curve to Active Curve\n"
342 "Needs an existing Active Curve")
343 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
347 description
="Type of bevel curve",
351 scale_x
: FloatProperty(
353 description
="Scale on X axis",
356 scale_y
: FloatProperty(
358 description
="Scale on Y axis",
363 description
="Link the Scale on X/Y axis",
366 edit_mode
: BoolProperty(
367 name
="Show in edit mode",
369 description
="Show in edit mode"
373 def poll(cls
, context
):
374 obj
= context
.active_object
375 return context
.mode
== 'OBJECT' and obj
and obj
.type == "CURVE"
377 def draw(self
, context
):
380 col
= layout
.column(align
=True)
381 # AddObjectHelper props
382 col
.prop(self
, "align")
383 col
.prop(self
, "location")
384 col
.prop(self
, "rotation")
386 col
= layout
.column(align
=True)
387 col
.label(text
= "Settings:")
388 col
.prop(self
, "types")
390 split
= layout
.split(factor
=0.95, align
=True)
391 col
= split
.column(align
=True)
392 col
.prop(self
, "scale_x")
393 row
= split
.row(align
=True)
395 col
.prop(self
, "scale_y")
396 row
.prop(self
, "link", toggle
=True, text
="", icon
="LINKED")
398 col
= layout
.column()
399 col
.row().prop(self
, "edit_mode", expand
=True)
401 def execute(self
, context
):
403 self
.scale_y
= self
.scale_x
405 add_type1(self
, context
)
407 add_type2(self
, context
)
409 add_type3(self
, context
)
411 add_type4(self
, context
)
413 add_type5(self
, context
)
416 bpy
.ops
.object.mode_set(mode
= 'EDIT')
418 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
423 def menu_funcs(self
, context
):
424 bl_label
= "Bevel/Taper"
428 if bpy
.context
.view_layer
.objects
.active
.type == "CURVE":
429 layout
.operator("curve.bevelcurve")
430 layout
.operator("curve.tapercurve")
440 from bpy
.utils
import register_class
444 bpy
.types
.VIEW3D_MT_object_context_menu
.prepend(menu_funcs
)
447 from bpy
.utils
import unregister_class
448 for cls
in reversed(classes
):
449 unregister_class(cls
)
451 bpy
.types
.VIEW3D_MT_object_context_menu
.remove(menu_funcs
)
453 if __name__
== "__main__":