Merge branch 'blender-v4.0-release'
[blender-addons.git] / add_curve_extra_objects / beveltaper_curve.py
blobfbcc2594138433dc296ce919db33ed0f8025fd93
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # DevBo Task: https://developer.blender.org/T37377
7 bl_info = {
8 "name": "Bevel/Taper Curve",
9 "author": "Cmomoney",
10 "version": (1, 2, 1),
11 "blender": (2, 80, 0),
12 "location": "View3D > Object > Bevel/Taper",
13 "description": "Adds bevel and/or taper curve to active curve",
14 "warning": "",
15 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
16 "category": "Curve",
20 import bpy
21 from bpy.types import (
22 Operator,
23 Menu,
25 from bpy.props import (
26 BoolProperty,
27 FloatProperty,
28 IntProperty,
30 from bpy_extras.object_utils import (
31 AddObjectHelper,
32 object_data_add,
36 def add_taper(self, context):
37 scale_ends1 = self.scale_ends1
38 scale_ends2 = self.scale_ends2
39 scale_mid = self.scale_mid
40 verts = [
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
53 verts = [
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]
63 lhandles = [
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)]
73 rhandles = [
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
89 verts = [
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]
95 lhandles = [
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)]
101 rhandles = [
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
113 verts = [
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]
119 lhandles = [
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)]
125 rhandles = [
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
137 verts = [
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]
143 lhandles = [
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)]
149 rhandles = [
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
161 verts = [
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]
167 lhandles = [
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)]
173 rhandles = [
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)):
206 c = 0
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]
217 c += 1
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",
234 default=0.0,
235 min=0.0
237 scale_ends2 : FloatProperty(
238 name="End Width Right",
239 description="Adjust right end taper",
240 default=0.0,
241 min=0.0
243 scale_mid : FloatProperty(
244 name="Center Width",
245 description="Adjust taper at center",
246 default=1.0,
247 min=0.0
249 link1 : BoolProperty(
250 name="Link Ends",
251 description="Link the End Width Left / Right settings\n"
252 "End Width Left will be editable ",
253 default=True
255 link2 : BoolProperty(
256 name="Link Ends / Center",
257 description="Link the End Widths with the Center Width",
258 default=False
260 diff : FloatProperty(
261 name="Difference",
262 default=1,
263 description="Difference between ends and center while linked"
265 edit_mode : BoolProperty(
266 name="Show in edit mode",
267 default=True,
268 description="Show in edit mode"
271 @classmethod
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):
277 layout = self.layout
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)
287 row.scale_y = 2.0
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)
298 row.scale_y = 2.0
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):
308 if self.link1:
309 self.scale_ends2 = self.scale_ends1
311 if self.link2:
312 self.scale_ends2 = self.scale_ends1 = self.scale_mid - self.diff
314 add_taper(self, context)
316 if self.edit_mode:
317 bpy.ops.object.mode_set(mode = 'EDIT')
318 else:
319 bpy.ops.object.mode_set(mode = 'OBJECT')
321 return {'FINISHED'}
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'}
331 types : IntProperty(
332 name="Type",
333 description="Type of bevel curve",
334 default=1,
335 min=1, max=5
337 scale_x : FloatProperty(
338 name="Scale X",
339 description="Scale on X axis",
340 default=1.0
342 scale_y : FloatProperty(
343 name="Scale Y",
344 description="Scale on Y axis",
345 default=1.0
347 link : BoolProperty(
348 name="Link XY",
349 description="Link the Scale on X/Y axis",
350 default=True
352 edit_mode : BoolProperty(
353 name="Show in edit mode",
354 default=True,
355 description="Show in edit mode"
358 @classmethod
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):
364 layout = self.layout
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)
380 row.scale_y = 2.0
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):
388 if self.link:
389 self.scale_y = self.scale_x
390 if self.types == 1:
391 add_type1(self, context)
392 if self.types == 2:
393 add_type2(self, context)
394 if self.types == 3:
395 add_type3(self, context)
396 if self.types == 4:
397 add_type4(self, context)
398 if self.types == 5:
399 add_type5(self, context)
401 if self.edit_mode:
402 bpy.ops.object.mode_set(mode = 'EDIT')
403 else:
404 bpy.ops.object.mode_set(mode = 'OBJECT')
406 return {'FINISHED'}
409 def menu_funcs(self, context):
410 bl_label = "Bevel/Taper"
412 layout = self.layout
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")
418 layout.separator()
420 # Register
421 classes = [
422 add_bevelcurve,
423 add_tapercurve
426 def register():
427 from bpy.utils import register_class
428 for cls in classes:
429 register_class(cls)
431 bpy.types.VIEW3D_MT_object_context_menu.prepend(menu_funcs)
433 def unregister():
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__":
441 register()