Fix: Node Wrangler: new reroutes offset when output hidden
[blender-addons.git] / add_curve_extra_objects / add_curve_spirofit_bouncespline.py
blob044d176122ab4383d6afe5181312dbf31a1161e8
1 # SPDX-License-Identifier: GPL-2.0-or-later
4 bl_info = {
5 "name": "SpiroFit, Bounce Spline, and Catenary",
6 "author": "Antonio Osprite, Liero, Atom, Jimmy Hazevoet",
7 "version": (0, 2, 2),
8 "blender": (2, 80, 0),
9 "location": "Add > Curve > Knots",
10 "description": "SpiroFit, BounceSpline and Catenary adds "
11 "splines to selected mesh or objects",
12 "warning": "",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
14 "category": "Object",
17 import bpy
18 from bpy.types import (
19 Operator,
20 Panel,
22 from bpy.props import (
23 BoolProperty,
24 EnumProperty,
25 FloatProperty,
26 IntProperty,
27 StringProperty,
29 from mathutils import (
30 Matrix,
31 Vector,
33 from math import (
34 sin, cos,
35 pi, sqrt,
36 pow, radians
38 import random as r
41 # ------------------------------------------------------------
42 # "Build a spiral that fit the active object"
43 # Spirofit, original blender 2.45 script by: Antonio Osprite
44 # http://www.kino3d.com/forum/viewtopic.php?t=5374
45 # ------------------------------------------------------------
46 def distance(v1, v2):
47 d = (Vector(v1) - Vector(v2)).length
48 return d
51 def spiral_point(step, radius, z_coord, spires, waves, wave_iscale, rndm):
52 x = radius * cos(spires * step) + (r.random() - 0.5) * rndm
53 y = radius * sin(spires * step) + (r.random() - 0.5) * rndm
54 z = z_coord + (cos(waves * step * pi) * wave_iscale) + (r.random() - 0.5) * rndm
55 return [x, y, z]
58 def spirofit_spline(obj,
59 spire_resolution=4,
60 spires=4,
61 offset=0.0,
62 waves=0,
63 wave_iscale=0.0,
64 rndm_spire=0.0,
65 direction=False,
66 map_method='RAYCAST'
69 points = []
70 bb = obj.bound_box
71 bb_xmin = min([v[0] for v in bb])
72 bb_ymin = min([v[1] for v in bb])
73 bb_zmin = min([v[2] for v in bb])
74 bb_xmax = max([v[0] for v in bb])
75 bb_ymax = max([v[1] for v in bb])
76 bb_zmax = max([v[2] for v in bb])
78 radius = distance([bb_xmax, bb_ymax, bb_zmin], [bb_xmin, bb_ymin, bb_zmin]) / 2.0
79 height = bb_zmax - bb_zmin
80 cx = (bb_xmax + bb_xmin) / 2.0
81 cy = (bb_ymax + bb_ymin) / 2.0
82 steps = spires * spire_resolution
84 for i in range(steps + 1):
85 t = bb_zmin + (2 * pi / steps) * i
86 z = bb_zmin + (float(height) / steps) * i
87 if direction:
88 t = -t
89 cp = spiral_point(t, radius, z, spires, waves, wave_iscale, rndm_spire)
91 if map_method == 'RAYCAST':
92 success, hit, nor, index = obj.ray_cast(Vector(cp), (Vector([cx, cy, z]) - Vector(cp)))
93 if success:
94 points.append((hit + offset * nor))
96 elif map_method == 'CLOSESTPOINT':
97 success, hit, nor, index = obj.closest_point_on_mesh(cp)
98 if success:
99 points.append((hit + offset * nor))
101 return points
104 class SpiroFitSpline(Operator):
105 bl_idname = "object.add_spirofit_spline"
106 bl_label = "SpiroFit"
107 bl_description = "Wrap selected mesh in a spiral"
108 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
110 map_method : EnumProperty(
111 name="Mapping",
112 default='RAYCAST',
113 description="Mapping method",
114 items=[('RAYCAST', 'Ray cast', 'Ray casting'),
115 ('CLOSESTPOINT', 'Closest point', 'Closest point on mesh')]
117 direction : BoolProperty(
118 name="Direction",
119 description="Spire direction",
120 default=False
122 spire_resolution : IntProperty(
123 name="Spire Resolution",
124 default=8,
125 min=3,
126 max=1024,
127 soft_max=128,
128 description="Number of steps for one turn"
130 spires : IntProperty(
131 name="Spires",
132 default=4,
133 min=1,
134 max=1024,
135 soft_max=128,
136 description="Number of turns"
138 offset : FloatProperty(
139 name="Offset",
140 default=0.0,
141 precision=3,
142 description="Use normal direction to offset spline"
144 waves : IntProperty(
145 name="Wave",
146 default=0,
147 min=0,
148 description="Wave amount"
150 wave_iscale : FloatProperty(
151 name="Wave Intensity",
152 default=0.0,
153 min=0.0,
154 precision=3,
155 description="Wave intensity scale"
157 rndm_spire : FloatProperty(
158 name="Randomise",
159 default=0.0,
160 min=0.0,
161 precision=3,
162 description="Randomise spire"
164 spline_name : StringProperty(
165 name="Name",
166 default="SpiroFit"
168 spline_type : EnumProperty(
169 name="Spline",
170 default='BEZIER',
171 description="Spline type",
172 items=[('POLY', 'Poly', 'Poly spline'),
173 ('BEZIER', 'Bezier', 'Bezier spline')]
175 resolution_u : IntProperty(
176 name="Resolution U",
177 default=12,
178 min=0,
179 max=64,
180 description="Curve resolution u"
182 bevel : FloatProperty(
183 name="Bevel Radius",
184 default=0.0,
185 min=0.0,
186 precision=3,
187 description="Bevel depth"
189 bevel_res : IntProperty(
190 name="Bevel Resolution",
191 default=0,
192 min=0,
193 max=32,
194 description="Bevel resolution"
196 extrude : FloatProperty(
197 name="Extrude",
198 default=0.0,
199 min=0.0,
200 precision=3,
201 description="Extrude amount"
203 twist_mode : EnumProperty(
204 name="Twisting",
205 default='MINIMUM',
206 description="Twist method, type of tilt calculation",
207 items=[('Z_UP', "Z-Up", 'Z Up'),
208 ('MINIMUM', "Minimum", 'Minimum'),
209 ('TANGENT', "Tangent", 'Tangent')]
211 twist_smooth : FloatProperty(
212 name="Smooth",
213 default=0.0,
214 min=0.0,
215 precision=3,
216 description="Twist smoothing amount for tangents"
218 tilt : FloatProperty(
219 name="Tilt",
220 default=0.0,
221 precision=3,
222 description="Spline handle tilt"
224 random_radius : FloatProperty(
225 name="Randomise",
226 default=0.0,
227 min=0.0,
228 precision=3,
229 description="Randomise radius of spline controlpoints"
231 random_seed : IntProperty(
232 name="Random Seed",
233 default=1,
234 min=0,
235 description="Random seed number"
237 origin_to_start : BoolProperty(
238 name="Origin at Start",
239 description="Set origin at first point of spline",
240 default=False
242 refresh : BoolProperty(
243 name="Refresh",
244 description="Refresh spline",
245 default=False
247 auto_refresh : BoolProperty(
248 name="Auto",
249 description="Auto refresh spline",
250 default=True
253 def draw(self, context):
254 layout = self.layout
255 col = layout.column(align=True)
256 row = col.row(align=True)
258 if self.auto_refresh is False:
259 self.refresh = False
260 elif self.auto_refresh is True:
261 self.refresh = True
263 row.prop(self, "auto_refresh", toggle=True, icon="AUTO", icon_only=True)
264 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH", icon_only=True)
265 row.operator("object.add_spirofit_spline", text="Add")
266 row.prop(self, "origin_to_start", toggle=True, icon="CURVE_DATA", icon_only=True)
268 col = layout.column(align=True)
269 col.prop(self, "spline_name")
270 col.separator()
271 col.prop(self, "map_method")
272 col.separator()
273 col.prop(self, "spire_resolution")
274 row = col.row(align=True).split(factor=0.9, align=True)
275 row.prop(self, "spires")
276 row.prop(self, "direction", toggle=True, text="", icon='ARROW_LEFTRIGHT')
277 col.prop(self, "offset")
278 col.prop(self, "waves")
279 col.prop(self, "wave_iscale")
280 col.prop(self, "rndm_spire")
281 col.prop(self, "random_seed")
282 draw_spline_settings(self)
284 @classmethod
285 def poll(self, context):
286 ob = context.active_object
287 return ((ob is not None) and
288 (context.mode == 'OBJECT'))
290 def invoke(self, context, event):
291 self.refresh = True
292 return self.execute(context)
294 def execute(self, context):
295 if not self.refresh:
296 return {'PASS_THROUGH'}
298 obj = context.active_object
299 if obj.type != 'MESH':
300 self.report({'WARNING'},
301 "Active Object is not a Mesh. Operation Cancelled")
302 return {'CANCELLED'}
304 bpy.ops.object.select_all(action='DESELECT')
306 r.seed(self.random_seed)
308 points = spirofit_spline(
309 obj,
310 self.spire_resolution,
311 self.spires,
312 self.offset,
313 self.waves,
314 self.wave_iscale,
315 self.rndm_spire,
316 self.direction,
317 self.map_method
320 add_curve_object(
321 points,
322 obj.matrix_world,
323 self.spline_name,
324 self.spline_type,
325 self.resolution_u,
326 self.bevel,
327 self.bevel_res,
328 self.extrude,
329 self.random_radius,
330 self.twist_mode,
331 self.twist_smooth,
332 self.tilt
335 if self.origin_to_start is True:
336 move_origin_to_start()
338 if self.auto_refresh is False:
339 self.refresh = False
341 return {'FINISHED'}
344 # ------------------------------------------------------------
345 # Bounce spline / Fiber mesh
346 # Original script by Liero and Atom
347 # https://blenderartists.org/forum/showthread.php?331750-Fiber-Mesh-Emulation
348 # ------------------------------------------------------------
349 def noise(var=1):
350 rand = Vector((r.gauss(0, 1), r.gauss(0, 1), r.gauss(0, 1)))
351 vec = rand.normalized() * var
352 return vec
355 def bounce_spline(obj,
356 number=1000,
357 ang_noise=0.25,
358 offset=0.0,
359 extra=50,
360 active_face=False
363 dist, points = 1000, []
364 poly = obj.data.polygons
366 if active_face:
367 try:
368 n = poly.active
369 except:
370 print("No active face selected")
371 pass
372 else:
373 n = r.randint(0, len(poly) - 1)
375 end = poly[n].normal.copy() * -1
376 start = poly[n].center
377 points.append(start + offset * end)
379 for i in range(number):
380 for ray in range(extra + 1):
381 end += noise(ang_noise)
382 try:
383 hit, nor, index = obj.ray_cast(start, end * dist)[-3:]
384 except:
385 index = -1
386 if index != -1:
387 start = hit - nor / 10000
388 end = end.reflect(nor).normalized()
389 points.append(hit + offset * nor)
390 break
391 if index == -1:
392 return points
393 return points
396 class BounceSpline(Operator):
397 bl_idname = "object.add_bounce_spline"
398 bl_label = "Bounce Spline"
399 bl_description = "Fill selected mesh with a spline"
400 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
402 bounce_number : IntProperty(
403 name="Bounces",
404 default=1000,
405 min=1,
406 max=100000,
407 soft_max=10000,
408 description="Number of bounces"
410 ang_noise : FloatProperty(
411 name="Angular Noise",
412 default=0.25,
413 min=0.0,
414 precision=3,
415 description="Add some noise to ray direction"
417 offset : FloatProperty(
418 name="Offset",
419 default=0.0,
420 precision=3,
421 description="Use normal direction to offset spline"
423 extra : IntProperty(
424 name="Extra",
425 default=50,
426 min=0,
427 max=1000,
428 description="Number of extra tries if it fails to hit mesh"
430 active_face : BoolProperty(
431 name="Active Face",
432 default=False,
433 description="Starts from active face or a random one"
435 spline_name : StringProperty(
436 name="Name",
437 default="BounceSpline"
439 spline_type : EnumProperty(
440 name="Spline",
441 default='BEZIER',
442 description="Spline type",
443 items=[('POLY', "Poly", "Poly spline"),
444 ('BEZIER', "Bezier", "Bezier spline")]
446 resolution_u : IntProperty(
447 name="Resolution U",
448 default=12,
449 min=0,
450 max=64,
451 description="Curve resolution u"
453 bevel : FloatProperty(
454 name="Bevel Radius",
455 default=0.0,
456 min=0.0,
457 precision=3,
458 description="Bevel depth"
460 bevel_res : IntProperty(
461 name="Bevel Resolution",
462 default=0,
463 min=0,
464 max=32,
465 description="Bevel resolution"
467 extrude : FloatProperty(
468 name="Extrude",
469 default=0.0,
470 min=0.0,
471 precision=3,
472 description="Extrude amount"
474 twist_mode : EnumProperty(
475 name="Twisting",
476 default='MINIMUM',
477 description="Twist method, type of tilt calculation",
478 items=[('Z_UP', "Z-Up", 'Z Up'),
479 ('MINIMUM', "Minimum", 'Minimum'),
480 ('TANGENT', "Tangent", 'Tangent')]
482 twist_smooth : FloatProperty(
483 name="Smooth",
484 default=0.0,
485 min=0.0,
486 precision=3,
487 description="Twist smoothing amount for tangents"
489 tilt : FloatProperty(
490 name="Tilt",
491 default=0.0,
492 precision=3,
493 description="Spline handle tilt"
495 random_radius : FloatProperty(
496 name="Randomise",
497 default=0.0,
498 min=0.0,
499 precision=3,
500 description="Randomise radius of spline controlpoints"
502 random_seed : IntProperty(
503 name="Random Seed",
504 default=1,
505 min=0,
506 description="Random seed number"
508 origin_to_start : BoolProperty(
509 name="Origin at Start",
510 description="Set origin at first point of spline",
511 default=False
513 refresh : BoolProperty(
514 name="Refresh",
515 description="Refresh spline",
516 default=False
518 auto_refresh : BoolProperty(
519 name="Auto",
520 description="Auto refresh spline",
521 default=True
524 def draw(self, context):
525 layout = self.layout
526 col = layout.column(align=True)
527 row = col.row(align=True)
528 if self.auto_refresh is False:
529 self.refresh = False
530 elif self.auto_refresh is True:
531 self.refresh = True
533 row.prop(self, "auto_refresh", toggle=True, icon="AUTO", icon_only=True)
534 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH", icon_only=True)
535 row.operator("object.add_bounce_spline", text="Add")
536 row.prop(self, "origin_to_start", toggle=True, icon="CURVE_DATA", icon_only=True)
538 col = layout.column(align=True)
539 col.prop(self, "spline_name")
540 col.separator()
541 col.prop(self, "bounce_number")
542 row = col.row(align=True).split(factor=0.9, align=True)
543 row.prop(self, "ang_noise")
544 row.prop(self, "active_face", toggle=True, text="", icon="SNAP_FACE")
545 col.prop(self, "offset")
546 col.prop(self, "extra")
547 col.prop(self, "random_seed")
548 draw_spline_settings(self)
550 @classmethod
551 def poll(self, context):
552 ob = context.active_object
553 return ((ob is not None) and
554 (context.mode == 'OBJECT'))
556 def invoke(self, context, event):
557 self.refresh = True
558 return self.execute(context)
560 def execute(self, context):
561 if not self.refresh:
562 return {'PASS_THROUGH'}
564 obj = context.active_object
565 if obj.type != 'MESH':
566 return {'CANCELLED'}
568 bpy.ops.object.select_all(action='DESELECT')
570 r.seed(self.random_seed)
572 points = bounce_spline(
573 obj,
574 self.bounce_number,
575 self.ang_noise,
576 self.offset,
577 self.extra,
578 self.active_face
581 add_curve_object(
582 points,
583 obj.matrix_world,
584 self.spline_name,
585 self.spline_type,
586 self.resolution_u,
587 self.bevel,
588 self.bevel_res,
589 self.extrude,
590 self.random_radius,
591 self.twist_mode,
592 self.twist_smooth,
593 self.tilt
596 if self.origin_to_start is True:
597 move_origin_to_start()
599 if self.auto_refresh is False:
600 self.refresh = False
602 return {'FINISHED'}
605 # ------------------------------------------------------------
606 # Hang Catenary curve between two selected objects
607 # ------------------------------------------------------------
608 def catenary_curve(
609 start=[-2, 0, 2],
610 end=[2, 0, 2],
611 steps=24,
612 a=2.0
615 points = []
616 lx = end[0] - start[0]
617 ly = end[1] - start[1]
618 lr = sqrt(pow(lx, 2) + pow(ly, 2))
619 lv = lr / 2 - (end[2] - start[2]) * a / lr
620 zv = start[2] - pow(lv, 2) / (2 * a)
621 slx = lx / steps
622 sly = ly / steps
623 slr = lr / steps
624 i = 0
625 while i <= steps:
626 x = start[0] + i * slx
627 y = start[1] + i * sly
628 z = zv + pow((i * slr) - lv, 2) / (2 * a)
629 points.append([x, y, z])
630 i += 1
631 return points
634 class CatenaryCurve(Operator):
635 bl_idname = "object.add_catenary_curve"
636 bl_label = "Catenary"
637 bl_description = "Hang a curve between two selected objects"
638 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
640 steps : IntProperty(
641 name="Steps",
642 description="Resolution of the curve",
643 default=24,
644 min=2,
645 max=1024,
647 var_a : FloatProperty(
648 name="a",
649 description="Catenary variable a",
650 precision=3,
651 default=2.0,
652 min=0.01,
653 max=100.0
655 spline_name : StringProperty(
656 name="Name",
657 default="Catenary"
659 spline_type : EnumProperty(
660 name="Spline",
661 default='BEZIER',
662 description="Spline type",
663 items=[('POLY', "Poly", "Poly spline"),
664 ('BEZIER', "Bezier", "Bezier spline")]
666 resolution_u : IntProperty(
667 name="Resolution U",
668 default=12,
669 min=0,
670 max=64,
671 description="Curve resolution u"
673 bevel : FloatProperty(
674 name="Bevel Radius",
675 default=0.0,
676 min=0.0,
677 precision=3,
678 description="Bevel depth"
680 bevel_res : IntProperty(
681 name="Bevel Resolution",
682 default=0,
683 min=0,
684 max=32,
685 description="Bevel resolution"
687 extrude : FloatProperty(
688 name="Extrude",
689 default=0.0,
690 min=0.0,
691 precision=3,
692 description="Extrude amount"
694 twist_mode : EnumProperty(
695 name="Twisting",
696 default='MINIMUM',
697 description="Twist method, type of tilt calculation",
698 items=[('Z_UP', "Z-Up", 'Z Up'),
699 ('MINIMUM', "Minimum", "Minimum"),
700 ('TANGENT', "Tangent", "Tangent")]
702 twist_smooth : FloatProperty(
703 name="Smooth",
704 default=0.0,
705 min=0.0,
706 precision=3,
707 description="Twist smoothing amount for tangents"
709 tilt : FloatProperty(
710 name="Tilt",
711 default=0.0,
712 precision=3,
713 description="Spline handle tilt"
715 random_radius : FloatProperty(
716 name="Randomise",
717 default=0.0,
718 min=0.0,
719 precision=3,
720 description="Randomise radius of spline controlpoints"
722 random_seed : IntProperty(
723 name="Random Seed",
724 default=1,
725 min=0,
726 description="Random seed number"
728 origin_to_start : BoolProperty(
729 name="Origin at Start",
730 description="Set origin at first point of spline",
731 default=False
733 refresh : BoolProperty(
734 name="Refresh",
735 description="Refresh spline",
736 default=False
738 auto_refresh : BoolProperty(
739 name="Auto",
740 description="Auto refresh spline",
741 default=True
744 def draw(self, context):
745 layout = self.layout
746 col = layout.column(align=True)
747 row = col.row(align=True)
749 if self.auto_refresh is False:
750 self.refresh = False
751 elif self.auto_refresh is True:
752 self.refresh = True
754 row.prop(self, "auto_refresh", toggle=True, icon="AUTO", icon_only=True)
755 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH", icon_only=True)
756 row.operator("object.add_catenary_curve", text="Add")
757 row.prop(self, "origin_to_start", toggle=True, icon="CURVE_DATA", icon_only=True)
759 col = layout.column(align=True)
760 col.prop(self, "spline_name")
761 col.separator()
762 col.prop(self, "steps")
763 col.prop(self, "var_a")
765 draw_spline_settings(self)
766 col = layout.column(align=True)
767 col.prop(self, "random_seed")
769 @classmethod
770 def poll(self, context):
771 ob = context.active_object
772 return ob is not None
774 def invoke(self, context, event):
775 self.refresh = True
776 return self.execute(context)
778 def execute(self, context):
779 if not self.refresh:
780 return {'PASS_THROUGH'}
782 try:
783 #ob1 = bpy.context.active_object
784 #ob1.select = False
785 ob1 = bpy.context.selected_objects[0]
786 ob2 = bpy.context.selected_objects[1]
788 start = ob1.location
789 end = ob2.location
790 if (start[0] == end[0]) and (start[1] == end[1]):
791 self.report({"WARNING"},
792 "Objects have the same X, Y location. Operation Cancelled")
794 return {'CANCELLED'}
795 except:
796 self.report({"WARNING"},
797 "Catenary could not be completed. Operation Cancelled")
798 return {'CANCELLED'}
800 bpy.ops.object.select_all(action='DESELECT')
802 r.seed(self.random_seed)
804 points = catenary_curve(
805 start,
806 end,
807 self.steps,
808 self.var_a
810 add_curve_object(
811 points,
812 Matrix(),
813 self.spline_name,
814 self.spline_type,
815 self.resolution_u,
816 self.bevel,
817 self.bevel_res,
818 self.extrude,
819 self.random_radius,
820 self.twist_mode,
821 self.twist_smooth,
822 self.tilt
825 if self.origin_to_start is True:
826 move_origin_to_start()
827 else:
828 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
830 if self.auto_refresh is False:
831 self.refresh = False
833 return {'FINISHED'}
836 # ------------------------------------------------------------
837 # Generate curve object from given points
838 # ------------------------------------------------------------
839 def add_curve_object(
840 verts,
841 matrix,
842 spline_name="Spline",
843 spline_type='BEZIER',
844 resolution_u=12,
845 bevel=0.0,
846 bevel_resolution=0,
847 extrude=0.0,
848 spline_radius=0.0,
849 twist_mode='MINIMUM',
850 twist_smooth=0.0,
851 tilt=0.0
854 scene = bpy.context.scene
855 vl = bpy.context.view_layer
856 curve = bpy.data.curves.new(spline_name, 'CURVE')
857 curve.dimensions = '3D'
858 spline = curve.splines.new(spline_type)
859 cur = bpy.data.objects.new(spline_name, curve)
860 spline.radius_interpolation = 'BSPLINE'
861 spline.tilt_interpolation = 'BSPLINE'
863 if spline_type == 'BEZIER':
864 spline.bezier_points.add(int(len(verts) - 1))
865 for i in range(len(verts)):
866 spline.bezier_points[i].co = verts[i]
867 spline.bezier_points[i].handle_right_type = 'AUTO'
868 spline.bezier_points[i].handle_left_type = 'AUTO'
869 spline.bezier_points[i].radius += spline_radius * r.random()
870 spline.bezier_points[i].tilt = radians(tilt)
871 else:
872 spline.points.add(int(len(verts) - 1))
873 for i in range(len(verts)):
874 spline.points[i].co = verts[i][0], verts[i][1], verts[i][2], 1
876 scene.collection.objects.link(cur)
877 cur.data.resolution_u = resolution_u
878 cur.data.fill_mode = 'FULL'
879 cur.data.bevel_depth = bevel
880 cur.data.bevel_resolution = bevel_resolution
881 cur.data.extrude = extrude
882 cur.data.twist_mode = twist_mode
883 cur.data.twist_smooth = twist_smooth
884 cur.matrix_world = matrix
885 cur.select_set(True)
886 vl.objects.active = cur
887 return
890 def move_origin_to_start():
891 active = bpy.context.active_object
892 spline = active.data.splines[0]
894 if spline.type == 'BEZIER':
895 start = active.matrix_world @ spline.bezier_points[0].co
896 else:
897 start = active.matrix_world @ spline.points[0].co
898 start = start[:-1]
900 cursor = bpy.context.scene.cursor.location.copy()
901 bpy.context.scene.cursor.location = start
902 bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
903 bpy.context.scene.cursor.location = cursor
906 def draw_spline_settings(self):
907 layout = self.layout
908 col = layout.column(align=True)
910 col.prop(self, "spline_type")
911 col.separator()
912 col.prop(self, "resolution_u")
913 col.prop(self, "bevel")
914 col.prop(self, "bevel_res")
915 col.prop(self, "extrude")
917 if self.spline_type == 'BEZIER':
918 col.prop(self, "random_radius")
919 col.separator()
920 col.prop(self, "twist_mode")
921 col.separator()
923 if self.twist_mode == 'TANGENT':
924 col.prop(self, "twist_smooth")
926 if self.spline_type == 'BEZIER':
927 col.prop(self, "tilt")
930 # ------------------------------------------------------------
931 # Register
932 # ------------------------------------------------------------
933 def register():
934 bpy.utils.register_class(SpiroFitSpline)
935 bpy.utils.register_class(BounceSpline)
936 bpy.utils.register_class(CatenaryCurve)
939 def unregister():
940 bpy.utils.unregister_class(SpiroFitSpline)
941 bpy.utils.unregister_class(BounceSpline)
942 bpy.utils.unregister_class(CatenaryCurve)
945 if __name__ == "__main__":
946 register()