Merge branch 'blender-v4.0-release'
[blender-addons.git] / add_curve_extra_objects / add_curve_spirofit_bouncespline.py
blobf1726731699ed4ac9f817ce92824e262018736fe
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 bl_info = {
7 "name": "SpiroFit, Bounce Spline, and Catenary",
8 "author": "Antonio Osprite, Liero, Atom, Jimmy Hazevoet",
9 "version": (0, 2, 2),
10 "blender": (2, 80, 0),
11 "location": "Add > Curve > Knots",
12 "description": "SpiroFit, BounceSpline and Catenary adds "
13 "splines to selected mesh or objects",
14 "warning": "",
15 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
16 "category": "Object",
19 import bpy
20 from bpy.types import (
21 Operator,
22 Panel,
24 from bpy.props import (
25 BoolProperty,
26 EnumProperty,
27 FloatProperty,
28 IntProperty,
29 StringProperty,
31 from mathutils import (
32 Matrix,
33 Vector,
35 from math import (
36 sin, cos,
37 pi, sqrt,
38 pow, radians
40 import random as r
43 # ------------------------------------------------------------
44 # "Build a spiral that fit the active object"
45 # Spirofit, original blender 2.45 script by: Antonio Osprite
46 # http://www.kino3d.com/forum/viewtopic.php?t=5374
47 # ------------------------------------------------------------
48 def distance(v1, v2):
49 d = (Vector(v1) - Vector(v2)).length
50 return d
53 def spiral_point(step, radius, z_coord, spires, waves, wave_iscale, rndm):
54 x = radius * cos(spires * step) + (r.random() - 0.5) * rndm
55 y = radius * sin(spires * step) + (r.random() - 0.5) * rndm
56 z = z_coord + (cos(waves * step * pi) * wave_iscale) + (r.random() - 0.5) * rndm
57 return [x, y, z]
60 def spirofit_spline(obj,
61 spire_resolution=4,
62 spires=4,
63 offset=0.0,
64 waves=0,
65 wave_iscale=0.0,
66 rndm_spire=0.0,
67 direction=False,
68 map_method='RAYCAST'
71 points = []
72 bb = obj.bound_box
73 bb_xmin = min([v[0] for v in bb])
74 bb_ymin = min([v[1] for v in bb])
75 bb_zmin = min([v[2] for v in bb])
76 bb_xmax = max([v[0] for v in bb])
77 bb_ymax = max([v[1] for v in bb])
78 bb_zmax = max([v[2] for v in bb])
80 radius = distance([bb_xmax, bb_ymax, bb_zmin], [bb_xmin, bb_ymin, bb_zmin]) / 2.0
81 height = bb_zmax - bb_zmin
82 cx = (bb_xmax + bb_xmin) / 2.0
83 cy = (bb_ymax + bb_ymin) / 2.0
84 steps = spires * spire_resolution
86 for i in range(steps + 1):
87 t = bb_zmin + (2 * pi / steps) * i
88 z = bb_zmin + (float(height) / steps) * i
89 if direction:
90 t = -t
91 cp = spiral_point(t, radius, z, spires, waves, wave_iscale, rndm_spire)
93 if map_method == 'RAYCAST':
94 success, hit, nor, index = obj.ray_cast(Vector(cp), (Vector([cx, cy, z]) - Vector(cp)))
95 if success:
96 points.append((hit + offset * nor))
98 elif map_method == 'CLOSESTPOINT':
99 success, hit, nor, index = obj.closest_point_on_mesh(cp)
100 if success:
101 points.append((hit + offset * nor))
103 return points
106 class SpiroFitSpline(Operator):
107 bl_idname = "object.add_spirofit_spline"
108 bl_label = "SpiroFit"
109 bl_description = "Wrap selected mesh in a spiral"
110 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
112 map_method : EnumProperty(
113 name="Mapping",
114 default='RAYCAST',
115 description="Mapping method",
116 items=[('RAYCAST', 'Ray cast', 'Ray casting'),
117 ('CLOSESTPOINT', 'Closest point', 'Closest point on mesh')]
119 direction : BoolProperty(
120 name="Direction",
121 description="Spire direction",
122 default=False
124 spire_resolution : IntProperty(
125 name="Spire Resolution",
126 default=8,
127 min=3,
128 max=1024,
129 soft_max=128,
130 description="Number of steps for one turn"
132 spires : IntProperty(
133 name="Spires",
134 default=4,
135 min=1,
136 max=1024,
137 soft_max=128,
138 description="Number of turns"
140 offset : FloatProperty(
141 name="Offset",
142 default=0.0,
143 precision=3,
144 description="Use normal direction to offset spline"
146 waves : IntProperty(
147 name="Wave",
148 default=0,
149 min=0,
150 description="Wave amount"
152 wave_iscale : FloatProperty(
153 name="Wave Intensity",
154 default=0.0,
155 min=0.0,
156 precision=3,
157 description="Wave intensity scale"
159 rndm_spire : FloatProperty(
160 name="Randomise",
161 default=0.0,
162 min=0.0,
163 precision=3,
164 description="Randomise spire"
166 spline_name : StringProperty(
167 name="Name",
168 default="SpiroFit"
170 spline_type : EnumProperty(
171 name="Spline",
172 default='BEZIER',
173 description="Spline type",
174 items=[('POLY', 'Poly', 'Poly spline'),
175 ('BEZIER', 'Bezier', 'Bezier spline')]
177 resolution_u : IntProperty(
178 name="Resolution U",
179 default=12,
180 min=0,
181 max=64,
182 description="Curve resolution u"
184 bevel : FloatProperty(
185 name="Bevel Radius",
186 default=0.0,
187 min=0.0,
188 precision=3,
189 description="Bevel depth"
191 bevel_res : IntProperty(
192 name="Bevel Resolution",
193 default=0,
194 min=0,
195 max=32,
196 description="Bevel resolution"
198 extrude : FloatProperty(
199 name="Extrude",
200 default=0.0,
201 min=0.0,
202 precision=3,
203 description="Extrude amount"
205 twist_mode : EnumProperty(
206 name="Twisting",
207 default='MINIMUM',
208 description="Twist method, type of tilt calculation",
209 items=[('Z_UP', "Z-Up", 'Z Up'),
210 ('MINIMUM', "Minimum", 'Minimum'),
211 ('TANGENT', "Tangent", 'Tangent')]
213 twist_smooth : FloatProperty(
214 name="Smooth",
215 default=0.0,
216 min=0.0,
217 precision=3,
218 description="Twist smoothing amount for tangents"
220 tilt : FloatProperty(
221 name="Tilt",
222 default=0.0,
223 precision=3,
224 description="Spline handle tilt"
226 random_radius : FloatProperty(
227 name="Randomise",
228 default=0.0,
229 min=0.0,
230 precision=3,
231 description="Randomise radius of spline controlpoints"
233 random_seed : IntProperty(
234 name="Random Seed",
235 default=1,
236 min=0,
237 description="Random seed number"
239 origin_to_start : BoolProperty(
240 name="Origin at Start",
241 description="Set origin at first point of spline",
242 default=False
244 refresh : BoolProperty(
245 name="Refresh",
246 description="Refresh spline",
247 default=False
249 auto_refresh : BoolProperty(
250 name="Auto",
251 description="Auto refresh spline",
252 default=True
255 def draw(self, context):
256 layout = self.layout
257 col = layout.column(align=True)
258 row = col.row(align=True)
260 if self.auto_refresh is False:
261 self.refresh = False
262 elif self.auto_refresh is True:
263 self.refresh = True
265 row.prop(self, "auto_refresh", toggle=True, icon="AUTO", icon_only=True)
266 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH", icon_only=True)
267 row.operator("object.add_spirofit_spline", text="Add")
268 row.prop(self, "origin_to_start", toggle=True, icon="CURVE_DATA", icon_only=True)
270 col = layout.column(align=True)
271 col.prop(self, "spline_name")
272 col.separator()
273 col.prop(self, "map_method")
274 col.separator()
275 col.prop(self, "spire_resolution")
276 row = col.row(align=True).split(factor=0.9, align=True)
277 row.prop(self, "spires")
278 row.prop(self, "direction", toggle=True, text="", icon='ARROW_LEFTRIGHT')
279 col.prop(self, "offset")
280 col.prop(self, "waves")
281 col.prop(self, "wave_iscale")
282 col.prop(self, "rndm_spire")
283 col.prop(self, "random_seed")
284 draw_spline_settings(self)
286 @classmethod
287 def poll(self, context):
288 ob = context.active_object
289 return ((ob is not None) and
290 (context.mode == 'OBJECT'))
292 def invoke(self, context, event):
293 self.refresh = True
294 return self.execute(context)
296 def execute(self, context):
297 if not self.refresh:
298 return {'PASS_THROUGH'}
300 obj = context.active_object
301 if obj.type != 'MESH':
302 self.report({'WARNING'},
303 "Active Object is not a Mesh. Operation Cancelled")
304 return {'CANCELLED'}
306 bpy.ops.object.select_all(action='DESELECT')
308 r.seed(self.random_seed)
310 points = spirofit_spline(
311 obj,
312 self.spire_resolution,
313 self.spires,
314 self.offset,
315 self.waves,
316 self.wave_iscale,
317 self.rndm_spire,
318 self.direction,
319 self.map_method
322 add_curve_object(
323 points,
324 obj.matrix_world,
325 self.spline_name,
326 self.spline_type,
327 self.resolution_u,
328 self.bevel,
329 self.bevel_res,
330 self.extrude,
331 self.random_radius,
332 self.twist_mode,
333 self.twist_smooth,
334 self.tilt
337 if self.origin_to_start is True:
338 move_origin_to_start()
340 if self.auto_refresh is False:
341 self.refresh = False
343 return {'FINISHED'}
346 # ------------------------------------------------------------
347 # Bounce spline / Fiber mesh
348 # Original script by Liero and Atom
349 # https://blenderartists.org/forum/showthread.php?331750-Fiber-Mesh-Emulation
350 # ------------------------------------------------------------
351 def noise(var=1):
352 rand = Vector((r.gauss(0, 1), r.gauss(0, 1), r.gauss(0, 1)))
353 vec = rand.normalized() * var
354 return vec
357 def bounce_spline(obj,
358 number=1000,
359 ang_noise=0.25,
360 offset=0.0,
361 extra=50,
362 active_face=False
365 dist, points = 1000, []
366 poly = obj.data.polygons
368 if active_face:
369 try:
370 n = poly.active
371 except:
372 print("No active face selected")
373 pass
374 else:
375 n = r.randint(0, len(poly) - 1)
377 end = poly[n].normal.copy() * -1
378 start = poly[n].center
379 points.append(start + offset * end)
381 for i in range(number):
382 for ray in range(extra + 1):
383 end += noise(ang_noise)
384 try:
385 hit, nor, index = obj.ray_cast(start, end * dist)[-3:]
386 except:
387 index = -1
388 if index != -1:
389 start = hit - nor / 10000
390 end = end.reflect(nor).normalized()
391 points.append(hit + offset * nor)
392 break
393 if index == -1:
394 return points
395 return points
398 class BounceSpline(Operator):
399 bl_idname = "object.add_bounce_spline"
400 bl_label = "Bounce Spline"
401 bl_description = "Fill selected mesh with a spline"
402 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
404 bounce_number : IntProperty(
405 name="Bounces",
406 default=1000,
407 min=1,
408 max=100000,
409 soft_max=10000,
410 description="Number of bounces"
412 ang_noise : FloatProperty(
413 name="Angular Noise",
414 default=0.25,
415 min=0.0,
416 precision=3,
417 description="Add some noise to ray direction"
419 offset : FloatProperty(
420 name="Offset",
421 default=0.0,
422 precision=3,
423 description="Use normal direction to offset spline"
425 extra : IntProperty(
426 name="Extra",
427 default=50,
428 min=0,
429 max=1000,
430 description="Number of extra tries if it fails to hit mesh"
432 active_face : BoolProperty(
433 name="Active Face",
434 default=False,
435 description="Starts from active face or a random one"
437 spline_name : StringProperty(
438 name="Name",
439 default="BounceSpline"
441 spline_type : EnumProperty(
442 name="Spline",
443 default='BEZIER',
444 description="Spline type",
445 items=[('POLY', "Poly", "Poly spline"),
446 ('BEZIER', "Bezier", "Bezier spline")]
448 resolution_u : IntProperty(
449 name="Resolution U",
450 default=12,
451 min=0,
452 max=64,
453 description="Curve resolution u"
455 bevel : FloatProperty(
456 name="Bevel Radius",
457 default=0.0,
458 min=0.0,
459 precision=3,
460 description="Bevel depth"
462 bevel_res : IntProperty(
463 name="Bevel Resolution",
464 default=0,
465 min=0,
466 max=32,
467 description="Bevel resolution"
469 extrude : FloatProperty(
470 name="Extrude",
471 default=0.0,
472 min=0.0,
473 precision=3,
474 description="Extrude amount"
476 twist_mode : EnumProperty(
477 name="Twisting",
478 default='MINIMUM',
479 description="Twist method, type of tilt calculation",
480 items=[('Z_UP', "Z-Up", 'Z Up'),
481 ('MINIMUM', "Minimum", 'Minimum'),
482 ('TANGENT', "Tangent", 'Tangent')]
484 twist_smooth : FloatProperty(
485 name="Smooth",
486 default=0.0,
487 min=0.0,
488 precision=3,
489 description="Twist smoothing amount for tangents"
491 tilt : FloatProperty(
492 name="Tilt",
493 default=0.0,
494 precision=3,
495 description="Spline handle tilt"
497 random_radius : FloatProperty(
498 name="Randomise",
499 default=0.0,
500 min=0.0,
501 precision=3,
502 description="Randomise radius of spline controlpoints"
504 random_seed : IntProperty(
505 name="Random Seed",
506 default=1,
507 min=0,
508 description="Random seed number"
510 origin_to_start : BoolProperty(
511 name="Origin at Start",
512 description="Set origin at first point of spline",
513 default=False
515 refresh : BoolProperty(
516 name="Refresh",
517 description="Refresh spline",
518 default=False
520 auto_refresh : BoolProperty(
521 name="Auto",
522 description="Auto refresh spline",
523 default=True
526 def draw(self, context):
527 layout = self.layout
528 col = layout.column(align=True)
529 row = col.row(align=True)
530 if self.auto_refresh is False:
531 self.refresh = False
532 elif self.auto_refresh is True:
533 self.refresh = True
535 row.prop(self, "auto_refresh", toggle=True, icon="AUTO", icon_only=True)
536 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH", icon_only=True)
537 row.operator("object.add_bounce_spline", text="Add")
538 row.prop(self, "origin_to_start", toggle=True, icon="CURVE_DATA", icon_only=True)
540 col = layout.column(align=True)
541 col.prop(self, "spline_name")
542 col.separator()
543 col.prop(self, "bounce_number")
544 row = col.row(align=True).split(factor=0.9, align=True)
545 row.prop(self, "ang_noise")
546 row.prop(self, "active_face", toggle=True, text="", icon="SNAP_FACE")
547 col.prop(self, "offset")
548 col.prop(self, "extra")
549 col.prop(self, "random_seed")
550 draw_spline_settings(self)
552 @classmethod
553 def poll(self, context):
554 ob = context.active_object
555 return ((ob is not None) and
556 (context.mode == 'OBJECT'))
558 def invoke(self, context, event):
559 self.refresh = True
560 return self.execute(context)
562 def execute(self, context):
563 if not self.refresh:
564 return {'PASS_THROUGH'}
566 obj = context.active_object
567 if obj.type != 'MESH':
568 return {'CANCELLED'}
570 bpy.ops.object.select_all(action='DESELECT')
572 r.seed(self.random_seed)
574 points = bounce_spline(
575 obj,
576 self.bounce_number,
577 self.ang_noise,
578 self.offset,
579 self.extra,
580 self.active_face
583 add_curve_object(
584 points,
585 obj.matrix_world,
586 self.spline_name,
587 self.spline_type,
588 self.resolution_u,
589 self.bevel,
590 self.bevel_res,
591 self.extrude,
592 self.random_radius,
593 self.twist_mode,
594 self.twist_smooth,
595 self.tilt
598 if self.origin_to_start is True:
599 move_origin_to_start()
601 if self.auto_refresh is False:
602 self.refresh = False
604 return {'FINISHED'}
607 # ------------------------------------------------------------
608 # Hang Catenary curve between two selected objects
609 # ------------------------------------------------------------
610 def catenary_curve(
611 start=[-2, 0, 2],
612 end=[2, 0, 2],
613 steps=24,
614 a=2.0
617 points = []
618 lx = end[0] - start[0]
619 ly = end[1] - start[1]
620 lr = sqrt(pow(lx, 2) + pow(ly, 2))
621 lv = lr / 2 - (end[2] - start[2]) * a / lr
622 zv = start[2] - pow(lv, 2) / (2 * a)
623 slx = lx / steps
624 sly = ly / steps
625 slr = lr / steps
626 i = 0
627 while i <= steps:
628 x = start[0] + i * slx
629 y = start[1] + i * sly
630 z = zv + pow((i * slr) - lv, 2) / (2 * a)
631 points.append([x, y, z])
632 i += 1
633 return points
636 class CatenaryCurve(Operator):
637 bl_idname = "object.add_catenary_curve"
638 bl_label = "Catenary"
639 bl_description = "Hang a curve between two selected objects"
640 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
642 steps : IntProperty(
643 name="Steps",
644 description="Resolution of the curve",
645 default=24,
646 min=2,
647 max=1024,
649 var_a : FloatProperty(
650 name="a",
651 description="Catenary variable a",
652 precision=3,
653 default=2.0,
654 min=0.01,
655 max=100.0
657 spline_name : StringProperty(
658 name="Name",
659 default="Catenary"
661 spline_type : EnumProperty(
662 name="Spline",
663 default='BEZIER',
664 description="Spline type",
665 items=[('POLY', "Poly", "Poly spline"),
666 ('BEZIER', "Bezier", "Bezier spline")]
668 resolution_u : IntProperty(
669 name="Resolution U",
670 default=12,
671 min=0,
672 max=64,
673 description="Curve resolution u"
675 bevel : FloatProperty(
676 name="Bevel Radius",
677 default=0.0,
678 min=0.0,
679 precision=3,
680 description="Bevel depth"
682 bevel_res : IntProperty(
683 name="Bevel Resolution",
684 default=0,
685 min=0,
686 max=32,
687 description="Bevel resolution"
689 extrude : FloatProperty(
690 name="Extrude",
691 default=0.0,
692 min=0.0,
693 precision=3,
694 description="Extrude amount"
696 twist_mode : EnumProperty(
697 name="Twisting",
698 default='MINIMUM',
699 description="Twist method, type of tilt calculation",
700 items=[('Z_UP', "Z-Up", 'Z Up'),
701 ('MINIMUM', "Minimum", "Minimum"),
702 ('TANGENT', "Tangent", "Tangent")]
704 twist_smooth : FloatProperty(
705 name="Smooth",
706 default=0.0,
707 min=0.0,
708 precision=3,
709 description="Twist smoothing amount for tangents"
711 tilt : FloatProperty(
712 name="Tilt",
713 default=0.0,
714 precision=3,
715 description="Spline handle tilt"
717 random_radius : FloatProperty(
718 name="Randomise",
719 default=0.0,
720 min=0.0,
721 precision=3,
722 description="Randomise radius of spline controlpoints"
724 random_seed : IntProperty(
725 name="Random Seed",
726 default=1,
727 min=0,
728 description="Random seed number"
730 origin_to_start : BoolProperty(
731 name="Origin at Start",
732 description="Set origin at first point of spline",
733 default=False
735 refresh : BoolProperty(
736 name="Refresh",
737 description="Refresh spline",
738 default=False
740 auto_refresh : BoolProperty(
741 name="Auto",
742 description="Auto refresh spline",
743 default=True
746 def draw(self, context):
747 layout = self.layout
748 col = layout.column(align=True)
749 row = col.row(align=True)
751 if self.auto_refresh is False:
752 self.refresh = False
753 elif self.auto_refresh is True:
754 self.refresh = True
756 row.prop(self, "auto_refresh", toggle=True, icon="AUTO", icon_only=True)
757 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH", icon_only=True)
758 row.operator("object.add_catenary_curve", text="Add")
759 row.prop(self, "origin_to_start", toggle=True, icon="CURVE_DATA", icon_only=True)
761 col = layout.column(align=True)
762 col.prop(self, "spline_name")
763 col.separator()
764 col.prop(self, "steps")
765 col.prop(self, "var_a")
767 draw_spline_settings(self)
768 col = layout.column(align=True)
769 col.prop(self, "random_seed")
771 @classmethod
772 def poll(self, context):
773 ob = context.active_object
774 return ob is not None
776 def invoke(self, context, event):
777 self.refresh = True
778 return self.execute(context)
780 def execute(self, context):
781 if not self.refresh:
782 return {'PASS_THROUGH'}
784 try:
785 #ob1 = bpy.context.active_object
786 #ob1.select = False
787 ob1 = bpy.context.selected_objects[0]
788 ob2 = bpy.context.selected_objects[1]
790 start = ob1.location
791 end = ob2.location
792 if (start[0] == end[0]) and (start[1] == end[1]):
793 self.report({"WARNING"},
794 "Objects have the same X, Y location. Operation Cancelled")
796 return {'CANCELLED'}
797 except:
798 self.report({"WARNING"},
799 "Catenary could not be completed. Operation Cancelled")
800 return {'CANCELLED'}
802 bpy.ops.object.select_all(action='DESELECT')
804 r.seed(self.random_seed)
806 points = catenary_curve(
807 start,
808 end,
809 self.steps,
810 self.var_a
812 add_curve_object(
813 points,
814 Matrix(),
815 self.spline_name,
816 self.spline_type,
817 self.resolution_u,
818 self.bevel,
819 self.bevel_res,
820 self.extrude,
821 self.random_radius,
822 self.twist_mode,
823 self.twist_smooth,
824 self.tilt
827 if self.origin_to_start is True:
828 move_origin_to_start()
829 else:
830 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
832 if self.auto_refresh is False:
833 self.refresh = False
835 return {'FINISHED'}
838 # ------------------------------------------------------------
839 # Generate curve object from given points
840 # ------------------------------------------------------------
841 def add_curve_object(
842 verts,
843 matrix,
844 spline_name="Spline",
845 spline_type='BEZIER',
846 resolution_u=12,
847 bevel=0.0,
848 bevel_resolution=0,
849 extrude=0.0,
850 spline_radius=0.0,
851 twist_mode='MINIMUM',
852 twist_smooth=0.0,
853 tilt=0.0
856 scene = bpy.context.scene
857 vl = bpy.context.view_layer
858 curve = bpy.data.curves.new(spline_name, 'CURVE')
859 curve.dimensions = '3D'
860 spline = curve.splines.new(spline_type)
861 cur = bpy.data.objects.new(spline_name, curve)
862 spline.radius_interpolation = 'BSPLINE'
863 spline.tilt_interpolation = 'BSPLINE'
865 if spline_type == 'BEZIER':
866 spline.bezier_points.add(int(len(verts) - 1))
867 for i in range(len(verts)):
868 spline.bezier_points[i].co = verts[i]
869 spline.bezier_points[i].handle_right_type = 'AUTO'
870 spline.bezier_points[i].handle_left_type = 'AUTO'
871 spline.bezier_points[i].radius += spline_radius * r.random()
872 spline.bezier_points[i].tilt = radians(tilt)
873 else:
874 spline.points.add(int(len(verts) - 1))
875 for i in range(len(verts)):
876 spline.points[i].co = verts[i][0], verts[i][1], verts[i][2], 1
878 scene.collection.objects.link(cur)
879 cur.data.resolution_u = resolution_u
880 cur.data.fill_mode = 'FULL'
881 cur.data.bevel_depth = bevel
882 cur.data.bevel_resolution = bevel_resolution
883 cur.data.extrude = extrude
884 cur.data.twist_mode = twist_mode
885 cur.data.twist_smooth = twist_smooth
886 cur.matrix_world = matrix
887 cur.select_set(True)
888 vl.objects.active = cur
889 return
892 def move_origin_to_start():
893 active = bpy.context.active_object
894 spline = active.data.splines[0]
896 if spline.type == 'BEZIER':
897 start = active.matrix_world @ spline.bezier_points[0].co
898 else:
899 start = active.matrix_world @ spline.points[0].co
900 start = start[:-1]
902 cursor = bpy.context.scene.cursor.location.copy()
903 bpy.context.scene.cursor.location = start
904 bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
905 bpy.context.scene.cursor.location = cursor
908 def draw_spline_settings(self):
909 layout = self.layout
910 col = layout.column(align=True)
912 col.prop(self, "spline_type")
913 col.separator()
914 col.prop(self, "resolution_u")
915 col.prop(self, "bevel")
916 col.prop(self, "bevel_res")
917 col.prop(self, "extrude")
919 if self.spline_type == 'BEZIER':
920 col.prop(self, "random_radius")
921 col.separator()
922 col.prop(self, "twist_mode")
923 col.separator()
925 if self.twist_mode == 'TANGENT':
926 col.prop(self, "twist_smooth")
928 if self.spline_type == 'BEZIER':
929 col.prop(self, "tilt")
932 # ------------------------------------------------------------
933 # Register
934 # ------------------------------------------------------------
935 def register():
936 bpy.utils.register_class(SpiroFitSpline)
937 bpy.utils.register_class(BounceSpline)
938 bpy.utils.register_class(CatenaryCurve)
941 def unregister():
942 bpy.utils.unregister_class(SpiroFitSpline)
943 bpy.utils.unregister_class(BounceSpline)
944 bpy.utils.unregister_class(CatenaryCurve)
947 if __name__ == "__main__":
948 register()