Merge branch 'blender-v3.6-release'
[blender-addons.git] / add_curve_extra_objects / add_curve_aceous_galore.py
blobf53b4d9793f852ad3aaa8eb3f7dd70b7264d7d2c
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """
4 bl_info = {
5 "name": "Curveaceous Galore!",
6 "author": "Jimmy Hazevoet, testscreenings",
7 "version": (0, 2, 3),
8 "blender": (2, 80),
9 "location": "View3D > Add > Curve",
10 "description": "Adds many different types of Curves",
11 "warning": "",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
13 "category": "Add Curve",
15 """
17 import bpy
18 from bpy_extras import object_utils
19 from bpy.props import (
20 BoolProperty,
21 EnumProperty,
22 FloatProperty,
23 IntProperty,
24 FloatVectorProperty
26 from mathutils import Matrix, Vector
27 from bpy.types import Operator
28 from math import (
29 sin, cos, pi
31 import mathutils.noise as Noise
34 # ------------------------------------------------------------
35 # Some functions to use with others:
36 # ------------------------------------------------------------
38 # ------------------------------------------------------------
39 # Generate random number:
40 def randnum(low=0.0, high=1.0, seed=0):
41 """
42 randnum( low=0.0, high=1.0, seed=0 )
44 Create random number
45 Parameters:
46 low - lower range
47 (type=float)
48 high - higher range
49 (type=float)
50 seed - the random seed number, if seed == 0, the current time will be used instead
51 (type=int)
52 Returns:
53 a random number
54 (type=float)
55 """
57 Noise.seed_set(seed)
58 rnum = Noise.random()
59 rnum = rnum * (high - low)
60 rnum = rnum + low
61 return rnum
64 # ------------------------------------------------------------
65 # Make some noise:
66 def vTurbNoise(x, y, z, iScale=0.25, Size=1.0, Depth=6, Hard=False, Basis=0, Seed=0):
67 """
68 vTurbNoise((x,y,z), iScale=0.25, Size=1.0, Depth=6, Hard=0, Basis=0, Seed=0 )
70 Create randomised vTurbulence noise
72 Parameters:
73 xyz - (x,y,z) float values.
74 (type=3-float tuple)
75 iScale - noise intensity scale
76 (type=float)
77 Size - noise size
78 (type=float)
79 Depth - number of noise values added.
80 (type=int)
81 Hard - noise hardness: True - soft noise; False - hard noise
82 (type=int)
83 basis - type of noise used for turbulence
84 (type=int)
85 Seed - the random seed number, if seed == 0, the current time will be used instead
86 (type=int)
87 Returns:
88 the generated turbulence vector.
89 (type=3-float list)
90 """
91 rand = randnum(-100, 100, Seed)
92 if Basis == 9:
93 Basis = 14
94 vec = Vector((x / Size + rand, y / Size + rand, z / Size + rand))
95 vTurb = Noise.turbulence_vector(vec, Depth, Hard)
96 #mathutils.noise.turbulence_vector(position, octaves, hard, noise_basis='PERLIN_ORIGINAL', amplitude_scale=0.5, frequency_scale=2.0)
97 tx = vTurb[0] * iScale
98 ty = vTurb[1] * iScale
99 tz = vTurb[2] * iScale
100 return tx, ty, tz
103 # -------------------------------------------------------------------
104 # 2D Curve shape functions:
105 # -------------------------------------------------------------------
107 # ------------------------------------------------------------
108 # 2DCurve: Profile: L, H, T, U, Z
109 def ProfileCurve(type=0, a=0.25, b=0.25):
111 ProfileCurve( type=0, a=0.25, b=0.25 )
113 Create profile curve
115 Parameters:
116 type - select profile type, L, H, T, U, Z
117 (type=int)
118 a - a scaling parameter
119 (type=float)
120 b - b scaling parameter
121 (type=float)
122 Returns:
123 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
124 (type=list)
127 newpoints = []
128 if type == 1:
129 # H:
130 a *= 0.5
131 b *= 0.5
132 newpoints = [
133 [-1.0, 1.0, 0.0], [-1.0 + a, 1.0, 0.0],
134 [-1.0 + a, b, 0.0], [1.0 - a, b, 0.0], [1.0 - a, 1.0, 0.0],
135 [1.0, 1.0, 0.0], [1.0, -1.0, 0.0], [1.0 - a, -1.0, 0.0],
136 [1.0 - a, -b, 0.0], [-1.0 + a, -b, 0.0], [-1.0 + a, -1.0, 0.0],
137 [-1.0, -1.0, 0.0]
139 elif type == 2:
140 # T:
141 a *= 0.5
142 newpoints = [
143 [-1.0, 1.0, 0.0], [1.0, 1.0, 0.0],
144 [1.0, 1.0 - b, 0.0], [a, 1.0 - b, 0.0], [a, -1.0, 0.0],
145 [-a, -1.0, 0.0], [-a, 1.0 - b, 0.0], [-1.0, 1.0 - b, 0.0]
147 elif type == 3:
148 # U:
149 a *= 0.5
150 newpoints = [
151 [-1.0, 1.0, 0.0], [-1.0 + a, 1.0, 0.0],
152 [-1.0 + a, -1.0 + b, 0.0], [1.0 - a, -1.0 + b, 0.0], [1.0 - a, 1.0, 0.0],
153 [1.0, 1.0, 0.0], [1.0, -1.0, 0.0], [-1.0, -1.0, 0.0]
155 elif type == 4:
156 # Z:
157 a *= 0.5
158 newpoints = [
159 [-0.5, 1.0, 0.0], [a, 1.0, 0.0],
160 [a, -1.0 + b, 0.0], [1.0, -1.0 + b, 0.0], [1.0, -1.0, 0.0],
161 [-a, -1.0, 0.0], [-a, 1.0 - b, 0.0], [-1.0, 1.0 - b, 0.0],
162 [-1.0, 1.0, 0.0]
164 else:
165 # L:
166 newpoints = [
167 [-1.0, 1.0, 0.0], [-1.0 + a, 1.0, 0.0],
168 [-1.0 + a, -1.0 + b, 0.0], [1.0, -1.0 + b, 0.0],
169 [1.0, -1.0, 0.0], [-1.0, -1.0, 0.0]
171 return newpoints
174 # ------------------------------------------------------------
175 # 2DCurve: Arrow
176 def ArrowCurve(type=1, a=1.0, b=0.5):
178 ArrowCurve( type=1, a=1.0, b=0.5, c=1.0 )
180 Create arrow curve
182 Parameters:
183 type - select type, Arrow1, Arrow2
184 (type=int)
185 a - a scaling parameter
186 (type=float)
187 b - b scaling parameter
188 (type=float)
189 Returns:
190 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
191 (type=list)
194 newpoints = []
195 if type == 0:
196 # Arrow1:
197 a *= 0.5
198 b *= 0.5
199 newpoints = [
200 [-1.0, b, 0.0], [-1.0 + a, b, 0.0],
201 [-1.0 + a, 1.0, 0.0], [1.0, 0.0, 0.0],
202 [-1.0 + a, -1.0, 0.0], [-1.0 + a, -b, 0.0],
203 [-1.0, -b, 0.0]
205 elif type == 1:
206 # Arrow2:
207 newpoints = [[-a, b, 0.0], [a, 0.0, 0.0], [-a, -b, 0.0], [0.0, 0.0, 0.0]]
208 else:
209 # diamond:
210 newpoints = [[0.0, b, 0.0], [a, 0.0, 0.0], [0.0, -b, 0.0], [-a, 0.0, 0.0]]
211 return newpoints
214 # ------------------------------------------------------------
215 # 2DCurve: Square / Rectangle
216 def RectCurve(type=1, a=1.0, b=0.5, c=1.0):
218 RectCurve( type=1, a=1.0, b=0.5, c=1.0 )
220 Create square / rectangle curve
222 Parameters:
223 type - select type, Square, Rounded square 1, Rounded square 2
224 (type=int)
225 a - a scaling parameter
226 (type=float)
227 b - b scaling parameter
228 (type=float)
229 c - c scaling parameter
230 (type=float)
231 Returns:
232 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
233 (type=list)
236 newpoints = []
237 if type == 1:
238 # Rounded Rectangle:
239 newpoints = [
240 [-a, b - b * 0.2, 0.0], [-a + a * 0.05, b - b * 0.05, 0.0], [-a + a * 0.2, b, 0.0],
241 [a - a * 0.2, b, 0.0], [a - a * 0.05, b - b * 0.05, 0.0], [a, b - b * 0.2, 0.0],
242 [a, -b + b * 0.2, 0.0], [a - a * 0.05, -b + b * 0.05, 0.0], [a - a * 0.2, -b, 0.0],
243 [-a + a * 0.2, -b, 0.0], [-a + a * 0.05, -b + b * 0.05, 0.0], [-a, -b + b * 0.2, 0.0]
245 elif type == 2:
246 # Rounded Rectangle II:
247 newpoints = []
248 x = a
249 y = b
250 r = c
251 if r > x:
252 r = x - 0.0001
253 if r > y:
254 r = y - 0.0001
255 if r > 0:
256 newpoints.append([-x + r, y, 0])
257 newpoints.append([x - r, y, 0])
258 newpoints.append([x, y - r, 0])
259 newpoints.append([x, -y + r, 0])
260 newpoints.append([x - r, -y, 0])
261 newpoints.append([-x + r, -y, 0])
262 newpoints.append([-x, -y + r, 0])
263 newpoints.append([-x, y - r, 0])
264 else:
265 newpoints.append([-x, y, 0])
266 newpoints.append([x, y, 0])
267 newpoints.append([x, -y, 0])
268 newpoints.append([-x, -y, 0])
269 else:
270 # Rectangle:
271 newpoints = [[-a, b, 0.0], [a, b, 0.0], [a, -b, 0.0], [-a, -b, 0.0]]
272 return newpoints
275 # ------------------------------------------------------------
276 # 2DCurve: Star:
277 def StarCurve(starpoints=8, innerradius=0.5, outerradius=1.0, twist=0.0):
279 StarCurve( starpoints=8, innerradius=0.5, outerradius=1.0, twist=0.0 )
281 Create star shaped curve
283 Parameters:
284 starpoints - the number of points
285 (type=int)
286 innerradius - innerradius
287 (type=float)
288 outerradius - outerradius
289 (type=float)
290 twist - twist amount
291 (type=float)
292 Returns:
293 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
294 (type=list)
297 newpoints = []
298 step = 2.0 / starpoints
299 i = 0
300 while i < starpoints:
301 t = i * step
302 x1 = cos(t * pi) * outerradius
303 y1 = sin(t * pi) * outerradius
304 newpoints.append([x1, y1, 0])
305 x2 = cos(t * pi + (pi / starpoints + twist)) * innerradius
306 y2 = sin(t * pi + (pi / starpoints + twist)) * innerradius
307 newpoints.append([x2, y2, 0])
308 i += 1
309 return newpoints
312 # ------------------------------------------------------------
313 # 2DCurve: Flower:
314 def FlowerCurve(petals=8, innerradius=0.5, outerradius=1.0, petalwidth=2.0):
316 FlowerCurve( petals=8, innerradius=0.5, outerradius=1.0, petalwidth=2.0 )
318 Create flower shaped curve
320 Parameters:
321 petals - the number of petals
322 (type=int)
323 innerradius - innerradius
324 (type=float)
325 outerradius - outerradius
326 (type=float)
327 petalwidth - width of petals
328 (type=float)
329 Returns:
330 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
331 (type=list)
334 newpoints = []
335 step = 2.0 / petals
336 pet = (step / pi * 2) * petalwidth
337 i = 0
338 while i < petals:
339 t = i * step
340 x1 = cos(t * pi - (pi / petals)) * innerradius
341 y1 = sin(t * pi - (pi / petals)) * innerradius
342 newpoints.append([x1, y1, 0])
343 x2 = cos(t * pi - pet) * outerradius
344 y2 = sin(t * pi - pet) * outerradius
345 newpoints.append([x2, y2, 0])
346 x3 = cos(t * pi + pet) * outerradius
347 y3 = sin(t * pi + pet) * outerradius
348 newpoints.append([x3, y3, 0])
349 i += 1
350 return newpoints
353 # ------------------------------------------------------------
354 # 2DCurve: Arc,Sector,Segment,Ring:
355 def ArcCurve(sides=6, startangle=0.0, endangle=90.0, innerradius=0.5, outerradius=1.0, type=3):
357 ArcCurve( sides=6, startangle=0.0, endangle=90.0, innerradius=0.5, outerradius=1.0, type=3 )
359 Create arc shaped curve
361 Parameters:
362 sides - number of sides
363 (type=int)
364 startangle - startangle
365 (type=float)
366 endangle - endangle
367 (type=float)
368 innerradius - innerradius
369 (type=float)
370 outerradius - outerradius
371 (type=float)
372 type - select type Arc,Sector,Segment,Ring
373 (type=int)
374 Returns:
375 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
376 (type=list)
379 newpoints = []
380 sides += 1
381 angle = 2.0 * (1.0 / 360.0)
382 endangle -= startangle
383 step = (angle * endangle) / (sides - 1)
384 i = 0
385 while i < sides:
386 t = (i * step) + angle * startangle
387 x1 = sin(t * pi) * outerradius
388 y1 = cos(t * pi) * outerradius
389 newpoints.append([x1, y1, 0])
390 i += 1
392 # if type == 1:
393 # Arc: turn cyclic curve flag off!
395 # Segment:
396 if type == 2:
397 newpoints.append([0, 0, 0])
398 # Ring:
399 elif type == 3:
400 j = sides - 1
401 while j > -1:
402 t = (j * step) + angle * startangle
403 x2 = sin(t * pi) * innerradius
404 y2 = cos(t * pi) * innerradius
405 newpoints.append([x2, y2, 0])
406 j -= 1
407 return newpoints
410 # ------------------------------------------------------------
411 # 2DCurve: Cog wheel:
412 def CogCurve(theeth=8, innerradius=0.8, middleradius=0.95, outerradius=1.0, bevel=0.5):
414 CogCurve( theeth=8, innerradius=0.8, middleradius=0.95, outerradius=1.0, bevel=0.5 )
416 Create cog wheel shaped curve
418 Parameters:
419 theeth - number of theeth
420 (type=int)
421 innerradius - innerradius
422 (type=float)
423 middleradius - middleradius
424 (type=float)
425 outerradius - outerradius
426 (type=float)
427 bevel - bevel amount
428 (type=float)
429 Returns:
430 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
431 (type=list)
434 newpoints = []
435 step = 2.0 / theeth
436 pet = step / pi * 2
437 bevel = 1.0 - bevel
438 i = 0
439 while i < theeth:
440 t = i * step
441 x1 = cos(t * pi - (pi / theeth) - pet) * innerradius
442 y1 = sin(t * pi - (pi / theeth) - pet) * innerradius
443 newpoints.append([x1, y1, 0])
444 x2 = cos(t * pi - (pi / theeth) + pet) * innerradius
445 y2 = sin(t * pi - (pi / theeth) + pet) * innerradius
446 newpoints.append([x2, y2, 0])
447 x3 = cos(t * pi - pet) * middleradius
448 y3 = sin(t * pi - pet) * middleradius
449 newpoints.append([x3, y3, 0])
450 x4 = cos(t * pi - (pet * bevel)) * outerradius
451 y4 = sin(t * pi - (pet * bevel)) * outerradius
452 newpoints.append([x4, y4, 0])
453 x5 = cos(t * pi + (pet * bevel)) * outerradius
454 y5 = sin(t * pi + (pet * bevel)) * outerradius
455 newpoints.append([x5, y5, 0])
456 x6 = cos(t * pi + pet) * middleradius
457 y6 = sin(t * pi + pet) * middleradius
458 newpoints.append([x6, y6, 0])
459 i += 1
460 return newpoints
463 # ------------------------------------------------------------
464 # 2DCurve: nSide:
465 def nSideCurve(sides=6, radius=1.0):
467 nSideCurve( sides=6, radius=1.0 )
469 Create n-sided curve
471 Parameters:
472 sides - number of sides
473 (type=int)
474 radius - radius
475 (type=float)
476 Returns:
477 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
478 (type=list)
481 newpoints = []
482 step = 2.0 / sides
483 i = 0
484 while i < sides:
485 t = i * step
486 x = sin(t * pi) * radius
487 y = cos(t * pi) * radius
488 newpoints.append([x, y, 0])
489 i += 1
490 return newpoints
493 # ------------------------------------------------------------
494 # 2DCurve: Splat:
495 def SplatCurve(sides=24, scale=1.0, seed=0, basis=0, radius=1.0):
497 SplatCurve( sides=24, scale=1.0, seed=0, basis=0, radius=1.0 )
499 Create splat curve
501 Parameters:
502 sides - number of sides
503 (type=int)
504 scale - noise size
505 (type=float)
506 seed - noise random seed
507 (type=int)
508 basis - noise basis
509 (type=int)
510 radius - radius
511 (type=float)
512 Returns:
513 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
514 (type=list)
517 newpoints = []
518 step = 2.0 / sides
519 i = 0
520 while i < sides:
521 t = i * step
522 turb = vTurbNoise(t, t, t, 1.0, scale, 6, False, basis, seed)
523 turb = turb[2] * 0.5 + 0.5
524 x = sin(t * pi) * radius * turb
525 y = cos(t * pi) * radius * turb
526 newpoints.append([x, y, 0])
527 i += 1
528 return newpoints
531 # -----------------------------------------------------------
532 # Cycloid curve
533 def CycloidCurve(number=100, type=0, R=4.0, r=1.0, d=1.0):
535 CycloidCurve( number=100, type=0, a=4.0, b=1.0 )
537 Create a Cycloid, Hypotrochoid / Hypocycloid or Epitrochoid / Epycycloid type of curve
539 Parameters:
540 number - the number of points
541 (type=int)
542 type - types: Cycloid, Hypocycloid, Epicycloid
543 (type=int)
544 R = Radius a scaling parameter
545 (type=float)
546 r = Radius b scaling parameter
547 (type=float)
548 d = Distance scaling parameter
549 (type=float)
550 Returns:
551 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
552 (type=list)
555 a = R
556 b = r
557 newpoints = []
558 step = 2.0 / (number - 1)
559 i = 0
560 if type == 1:
561 # Hypotrochoid / Hypocycloid
562 while i < number:
563 t = i * step
564 x = ((a - b) * cos(t * pi)) + (d * cos(((a + b) / b) * t * pi))
565 y = ((a - b) * sin(t * pi)) - (d * sin(((a + b) / b) * t * pi))
566 z = 0
567 newpoints.append([x, y, z])
568 i += 1
569 elif type == 2:
570 # Epitrochoid / Epycycloid
571 while i < number:
572 t = i * step
573 x = ((a + b) * cos(t * pi)) - (d * cos(((a + b) / b) * t * pi))
574 y = ((a + b) * sin(t * pi)) - (d * sin(((a + b) / b) * t * pi))
575 z = 0
576 newpoints.append([x, y, z])
577 i += 1
578 else:
579 # Cycloid
580 while i < number:
581 t = (i * step * pi)
582 x = (t - sin(t) * b) * a / pi
583 y = (1 - cos(t) * b) * a / pi
584 z = 0
585 newpoints.append([x, y, z])
586 i += 1
587 return newpoints
590 # -----------------------------------------------------------
591 # 3D curve shape functions:
592 # -----------------------------------------------------------
594 # ------------------------------------------------------------
595 # 3DCurve: Helix:
596 def HelixCurve(number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0):
598 HelixCurve( number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0 )
600 Create helix curve
602 Parameters:
603 number - the number of points
604 (type=int)
605 height - height
606 (type=float)
607 startangle - startangle
608 (type=float)
609 endangle - endangle
610 (type=float)
611 width - width
612 (type=float)
613 a - a
614 (type=float)
615 b - b
616 (type=float)
617 Returns:
618 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
619 (type=list)
622 newpoints = []
623 angle = (2.0 / 360.0) * (endangle - startangle)
624 step = angle / (number - 1)
625 h = height / angle
626 start = startangle * 2.0 / 360.0
627 a /= angle
628 i = 0
629 while i < number:
630 t = (i * step + start)
631 x = sin((t * pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width)
632 y = cos((t * pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width)
633 z = (t * h) - h * start
634 newpoints.append([x, y, z])
635 i += 1
636 return newpoints
639 # -----------------------------------------------------------
640 # 3D Noise curve
641 def NoiseCurve(type=0, number=100, length=2.0, size=0.5,
642 scale=[0.5, 0.5, 0.5], octaves=2, basis=0, seed=0):
644 Create noise curve
646 Parameters:
647 number - number of points
648 (type=int)
649 length - curve length
650 (type=float)
651 size - noise size
652 (type=float)
653 scale - noise intensity scale x,y,z
654 (type=list)
655 basis - noise basis
656 (type=int)
657 seed - noise random seed
658 (type=int)
659 type - noise curve type
660 (type=int)
661 Returns:
662 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
663 (type=list)
666 newpoints = []
667 step = (length / number)
668 i = 0
669 if type == 1:
670 # noise circle
671 while i < number:
672 t = i * step
673 v = vTurbNoise(t, t, t, 1.0, size, octaves, False, basis, seed)
674 x = sin(t * pi) + (v[0] * scale[0])
675 y = cos(t * pi) + (v[1] * scale[1])
676 z = v[2] * scale[2]
677 newpoints.append([x, y, z])
678 i += 1
679 elif type == 2:
680 # noise knot / ball
681 while i < number:
682 t = i * step
683 v = vTurbNoise(t, t, t, 1.0, 1.0, octaves, False, basis, seed)
684 x = v[0] * scale[0] * size
685 y = v[1] * scale[1] * size
686 z = v[2] * scale[2] * size
687 newpoints.append([x, y, z])
688 i += 1
689 else:
690 # noise linear
691 while i < number:
692 t = i * step
693 v = vTurbNoise(t, t, t, 1.0, size, octaves, False, basis, seed)
694 x = t + v[0] * scale[0]
695 y = v[1] * scale[1]
696 z = v[2] * scale[2]
697 newpoints.append([x, y, z])
698 i += 1
699 return newpoints
702 # get array of vertcoordinates according to splinetype
703 def vertsToPoints(Verts, splineType):
705 # main vars
706 vertArray = []
708 # array for BEZIER spline output (V3)
709 if splineType == 'BEZIER':
710 for v in Verts:
711 vertArray += v
713 # array for nonBEZIER output (V4)
714 else:
715 for v in Verts:
716 vertArray += v
717 if splineType == 'NURBS':
718 # for nurbs w=1
719 vertArray.append(1)
720 else:
721 # for poly w=0
722 vertArray.append(0)
723 return vertArray
726 # create new CurveObject from vertarray and splineType
727 def createCurve(context, vertArray, self):
728 # output splineType 'POLY' 'NURBS' 'BEZIER'
729 splineType = self.outputType
731 # GalloreType as name
732 name = self.ProfileType
734 # create object
735 if bpy.context.mode == 'EDIT_CURVE':
736 Curve = context.active_object
737 newSpline = Curve.data.splines.new(type=splineType) # spline
738 else:
739 # create curve
740 dataCurve = bpy.data.curves.new(name, type='CURVE') # curve data block
741 newSpline = dataCurve.splines.new(type=splineType) # spline
743 # create object with newCurve
744 Curve = object_utils.object_data_add(context, dataCurve, operator=self) # place in active scene
746 # set newSpline Options
747 newSpline.use_cyclic_u = self.use_cyclic_u
748 newSpline.use_endpoint_u = self.endp_u
749 newSpline.order_u = self.order_u
751 # set curve Options
752 Curve.data.dimensions = self.shape
753 Curve.data.use_path = True
754 if self.shape == '3D':
755 Curve.data.fill_mode = 'FULL'
756 else:
757 Curve.data.fill_mode = 'BOTH'
759 for spline in Curve.data.splines:
760 if spline.type == 'BEZIER':
761 for point in spline.bezier_points:
762 point.select_control_point = False
763 point.select_left_handle = False
764 point.select_right_handle = False
765 else:
766 for point in spline.points:
767 point.select = False
769 # create spline from vertarray
770 if splineType == 'BEZIER':
771 newSpline.bezier_points.add(int(len(vertArray) * 0.33))
772 newSpline.bezier_points.foreach_set('co', vertArray)
773 for point in newSpline.bezier_points:
774 point.handle_right_type = self.handleType
775 point.handle_left_type = self.handleType
776 point.select_control_point = True
777 point.select_left_handle = True
778 point.select_right_handle = True
779 else:
780 newSpline.points.add(int(len(vertArray) * 0.25 - 1))
781 newSpline.points.foreach_set('co', vertArray)
782 newSpline.use_endpoint_u = True
783 for point in newSpline.points:
784 point.select = True
786 # move and rotate spline in edit mode
787 if bpy.context.mode == 'EDIT_CURVE':
788 if self.align == "WORLD":
789 location = self.location - context.active_object.location
790 bpy.ops.transform.translate(value = location, orient_type='GLOBAL')
791 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X', orient_type='GLOBAL')
792 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y', orient_type='GLOBAL')
793 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z', orient_type='GLOBAL')
795 elif self.align == "VIEW":
796 bpy.ops.transform.translate(value = self.location)
797 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
798 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
799 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
801 elif self.align == "CURSOR":
802 location = context.active_object.location
803 self.location = bpy.context.scene.cursor.location - location
804 self.rotation = bpy.context.scene.cursor.rotation_euler
806 bpy.ops.transform.translate(value = self.location)
807 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
808 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
809 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
811 return
814 # ------------------------------------------------------------
815 # Main Function
816 def main(context, self):
817 # options
818 proType = self.ProfileType
819 splineType = self.outputType
820 innerRadius = self.innerRadius
821 middleRadius = self.middleRadius
822 outerRadius = self.outerRadius
824 # get verts
825 if proType == 'Profile':
826 verts = ProfileCurve(
827 self.ProfileCurveType,
828 self.ProfileCurvevar1,
829 self.ProfileCurvevar2
831 if proType == 'Arrow':
832 verts = ArrowCurve(
833 self.MiscCurveType,
834 self.MiscCurvevar1,
835 self.MiscCurvevar2
837 if proType == 'Rectangle':
838 verts = RectCurve(
839 self.MiscCurveType,
840 self.MiscCurvevar1,
841 self.MiscCurvevar2,
842 self.MiscCurvevar3
844 if proType == 'Flower':
845 verts = FlowerCurve(
846 self.petals,
847 innerRadius,
848 outerRadius,
849 self.petalWidth
851 if proType == 'Star':
852 verts = StarCurve(
853 self.starPoints,
854 innerRadius,
855 outerRadius,
856 self.starTwist
858 if proType == 'Arc':
859 verts = ArcCurve(
860 self.arcSides,
861 self.startAngle,
862 self.endAngle,
863 innerRadius,
864 outerRadius,
865 self.arcType
867 if proType == 'Cogwheel':
868 verts = CogCurve(
869 self.teeth,
870 innerRadius,
871 middleRadius,
872 outerRadius,
873 self.bevel
875 if proType == 'Nsided':
876 verts = nSideCurve(
877 self.Nsides,
878 outerRadius
880 if proType == 'Splat':
881 verts = SplatCurve(
882 self.splatSides,
883 self.splatScale,
884 self.seed,
885 self.basis,
886 outerRadius
888 if proType == 'Cycloid':
889 verts = CycloidCurve(
890 self.cycloPoints,
891 self.cycloType,
892 self.cyclo_a,
893 self.cyclo_b,
894 self.cyclo_d
896 if proType == 'Helix':
897 verts = HelixCurve(
898 self.helixPoints,
899 self.helixHeight,
900 self.helixStart,
901 self.helixEnd,
902 self.helixWidth,
903 self.helix_a,
904 self.helix_b
906 if proType == 'Noise':
907 verts = NoiseCurve(
908 self.noiseType,
909 self.noisePoints,
910 self.noiseLength,
911 self.noiseSize,
912 [self.noiseScaleX, self.noiseScaleY, self.noiseScaleZ],
913 self.noiseOctaves,
914 self.noiseBasis,
915 self.noiseSeed
918 # turn verts into array
919 vertArray = vertsToPoints(verts, splineType)
921 # create object
922 createCurve(context, vertArray, self)
924 return
927 class Curveaceous_galore(Operator, object_utils.AddObjectHelper):
928 bl_idname = "curve.curveaceous_galore"
929 bl_label = "Curve Profiles"
930 bl_description = "Construct many types of curves"
931 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
933 # general properties
934 ProfileType : EnumProperty(
935 name="Type",
936 description="Form of Curve to create",
937 items=[
938 ('Arc', "Arc", "Arc"),
939 ('Arrow', "Arrow", "Arrow"),
940 ('Cogwheel', "Cogwheel", "Cogwheel"),
941 ('Cycloid', "Cycloid", "Cycloid"),
942 ('Flower', "Flower", "Flower"),
943 ('Helix', "Helix (3D)", "Helix"),
944 ('Noise', "Noise (3D)", "Noise"),
945 ('Nsided', "Nsided", "Nsided"),
946 ('Profile', "Profile", "Profile"),
947 ('Rectangle', "Rectangle", "Rectangle"),
948 ('Splat', "Splat", "Splat"),
949 ('Star', "Star", "Star")]
951 outputType : EnumProperty(
952 name="Output splines",
953 description="Type of splines to output",
954 items=[
955 ('POLY', "Poly", "Poly Spline type"),
956 ('NURBS', "Nurbs", "Nurbs Spline type"),
957 ('BEZIER', "Bezier", "Bezier Spline type")]
959 # Curve Options
960 shape : EnumProperty(
961 name="2D / 3D",
962 description="2D or 3D Curve",
963 items=[
964 ('2D', "2D", "2D"),
965 ('3D', "3D", "3D")
968 use_cyclic_u : BoolProperty(
969 name="Cyclic",
970 default=True,
971 description="make curve closed"
973 endp_u : BoolProperty(
974 name="Use endpoint u",
975 default=True,
976 description="stretch to endpoints"
978 order_u : IntProperty(
979 name="Order u",
980 default=4,
981 min=2, soft_min=2,
982 max=6, soft_max=6,
983 description="Order of nurbs spline"
985 handleType : EnumProperty(
986 name="Handle type",
987 default='AUTO',
988 description="Bezier handles type",
989 items=[
990 ('VECTOR', "Vector", "Vector type Bezier handles"),
991 ('AUTO', "Auto", "Automatic type Bezier handles")]
993 # ProfileCurve properties
994 ProfileCurveType : IntProperty(
995 name="Type",
996 min=1,
997 max=5,
998 default=1,
999 description="Type of Curve's Profile"
1001 ProfileCurvevar1 : FloatProperty(
1002 name="Variable 1",
1003 default=0.25,
1004 description="Variable 1 of Curve's Profile"
1006 ProfileCurvevar2 : FloatProperty(
1007 name="Variable 2",
1008 default=0.25,
1009 description="Variable 2 of Curve's Profile"
1011 # Arrow, Rectangle, MiscCurve properties
1012 MiscCurveType : IntProperty(
1013 name="Type",
1014 min=0,
1015 max=3,
1016 default=0,
1017 description="Type of Curve"
1019 MiscCurvevar1 : FloatProperty(
1020 name="Variable 1",
1021 default=1.0,
1022 description="Variable 1 of Curve"
1024 MiscCurvevar2 : FloatProperty(
1025 name="Variable 2",
1026 default=0.5,
1027 description="Variable 2 of Curve"
1029 MiscCurvevar3 : FloatProperty(
1030 name="Variable 3",
1031 default=0.1,
1032 min=0,
1033 description="Variable 3 of Curve"
1035 # Common properties
1036 innerRadius : FloatProperty(
1037 name="Inner radius",
1038 default=0.5,
1039 min=0,
1040 description="Inner radius"
1042 middleRadius : FloatProperty(
1043 name="Middle radius",
1044 default=0.95,
1045 min=0,
1046 description="Middle radius"
1048 outerRadius : FloatProperty(
1049 name="Outer radius",
1050 default=1.0,
1051 min=0,
1052 description="Outer radius"
1054 # Flower properties
1055 petals : IntProperty(
1056 name="Petals",
1057 default=8,
1058 min=2,
1059 description="Number of petals"
1061 petalWidth : FloatProperty(
1062 name="Petal width",
1063 default=2.0,
1064 min=0.01,
1065 description="Petal width"
1067 # Star properties
1068 starPoints : IntProperty(
1069 name="Star points",
1070 default=8,
1071 min=2,
1072 description="Number of star points"
1074 starTwist : FloatProperty(
1075 name="Twist",
1076 default=0.0,
1077 description="Twist"
1079 # Arc properties
1080 arcSides : IntProperty(
1081 name="Arc sides",
1082 default=6,
1083 min=1,
1084 description="Sides of arc"
1086 startAngle : FloatProperty(
1087 name="Start angle",
1088 default=0.0,
1089 description="Start angle"
1091 endAngle : FloatProperty(
1092 name="End angle",
1093 default=90.0,
1094 description="End angle"
1096 arcType : IntProperty(
1097 name="Arc type",
1098 default=3,
1099 min=1,
1100 max=3,
1101 description="Sides of arc"
1103 # Cogwheel properties
1104 teeth : IntProperty(
1105 name="Teeth",
1106 default=8,
1107 min=2,
1108 description="number of teeth"
1110 bevel : FloatProperty(
1111 name="Bevel",
1112 default=0.5,
1113 min=0,
1114 max=1,
1115 description="Bevel"
1117 # Nsided property
1118 Nsides : IntProperty(
1119 name="Sides",
1120 default=8,
1121 min=3,
1122 description="Number of sides"
1124 # Splat properties
1125 splatSides : IntProperty(
1126 name="Splat sides",
1127 default=24,
1128 min=3,
1129 description="Splat sides"
1131 splatScale : FloatProperty(
1132 name="Splat scale",
1133 default=1.0,
1134 min=0.0001,
1135 description="Splat scale"
1137 seed : IntProperty(
1138 name="Seed",
1139 default=0,
1140 min=0,
1141 description="Seed"
1143 basis : IntProperty(
1144 name="Basis",
1145 default=0,
1146 min=0,
1147 max=14,
1148 description="Basis"
1150 # Helix properties
1151 helixPoints : IntProperty(
1152 name="Resolution",
1153 default=100,
1154 min=3,
1155 description="Resolution"
1157 helixHeight : FloatProperty(
1158 name="Height",
1159 default=2.0,
1160 min=0,
1161 description="Helix height"
1163 helixStart : FloatProperty(
1164 name="Start angle",
1165 default=0.0,
1166 description="Helix start angle"
1168 helixEnd : FloatProperty(
1169 name="Endangle",
1170 default=360.0,
1171 description="Helix end angle"
1173 helixWidth : FloatProperty(
1174 name="Width",
1175 default=1.0,
1176 description="Helix width"
1178 helix_a : FloatProperty(
1179 name="Variable 1",
1180 default=0.0,
1181 description="Helix Variable 1"
1183 helix_b : FloatProperty(
1184 name="Variable 2",
1185 default=0.0,
1186 description="Helix Variable 2"
1188 # Cycloid properties
1189 cycloPoints : IntProperty(
1190 name="Resolution",
1191 default=100,
1192 min=3,
1193 soft_min=3,
1194 description="Resolution"
1196 cycloType : IntProperty(
1197 name="Type",
1198 default=1,
1199 min=0,
1200 max=2,
1201 description="Type: Cycloid , Hypocycloid / Hypotrochoid , Epicycloid / Epitrochoid"
1203 cyclo_a : FloatProperty(
1204 name="R",
1205 default=1.0,
1206 min=0.01,
1207 description="Cycloid: R radius a"
1209 cyclo_b : FloatProperty(
1210 name="r",
1211 default=0.25,
1212 min=0.01,
1213 description="Cycloid: r radius b"
1215 cyclo_d : FloatProperty(
1216 name="d",
1217 default=0.25,
1218 description="Cycloid: d distance"
1220 # Noise properties
1221 noiseType : IntProperty(
1222 name="Type",
1223 default=0,
1224 min=0,
1225 max=2,
1226 description="Noise curve type: Linear, Circular or Knot"
1228 noisePoints : IntProperty(
1229 name="Resolution",
1230 default=100,
1231 min=3,
1232 description="Resolution"
1234 noiseLength : FloatProperty(
1235 name="Length",
1236 default=2.0,
1237 min=0.01,
1238 description="Curve Length"
1240 noiseSize : FloatProperty(
1241 name="Noise size",
1242 default=1.0,
1243 min=0.0001,
1244 description="Noise size"
1246 noiseScaleX : FloatProperty(
1247 name="Noise x",
1248 default=1.0,
1249 min=0.0001,
1250 description="Noise x"
1252 noiseScaleY : FloatProperty(
1253 name="Noise y",
1254 default=1.0,
1255 min=0.0001,
1256 description="Noise y"
1258 noiseScaleZ : FloatProperty(
1259 name="Noise z",
1260 default=1.0,
1261 min=0.0001,
1262 description="Noise z"
1264 noiseOctaves : IntProperty(
1265 name="Octaves",
1266 default=2,
1267 min=1,
1268 max=16,
1269 description="Basis"
1271 noiseBasis : IntProperty(
1272 name="Basis",
1273 default=0,
1274 min=0,
1275 max=9,
1276 description="Basis"
1278 noiseSeed : IntProperty(
1279 name="Seed",
1280 default=1,
1281 min=0,
1282 description="Random Seed"
1285 edit_mode : BoolProperty(
1286 name="Show in edit mode",
1287 default=True,
1288 description="Show in edit mode"
1291 def draw(self, context):
1292 layout = self.layout
1294 # general options
1295 col = layout.column()
1296 col.prop(self, 'ProfileType')
1297 col.label(text=self.ProfileType + " Options:")
1299 # options per ProfileType
1300 box = layout.box()
1301 col = box.column(align=True)
1303 if self.ProfileType == 'Profile':
1304 col.prop(self, "ProfileCurveType")
1305 col.prop(self, "ProfileCurvevar1")
1306 col.prop(self, "ProfileCurvevar2")
1308 elif self.ProfileType == 'Arrow':
1309 col.prop(self, "MiscCurveType")
1310 col.prop(self, "MiscCurvevar1", text="Height")
1311 col.prop(self, "MiscCurvevar2", text="Width")
1313 elif self.ProfileType == 'Rectangle':
1314 col.prop(self, "MiscCurveType")
1315 col.prop(self, "MiscCurvevar1", text="Width")
1316 col.prop(self, "MiscCurvevar2", text="Height")
1317 if self.MiscCurveType == 2:
1318 col.prop(self, "MiscCurvevar3", text="Corners")
1320 elif self.ProfileType == 'Flower':
1321 col.prop(self, "petals")
1322 col.prop(self, "petalWidth")
1324 col = box.column(align=True)
1325 col.prop(self, "innerRadius")
1326 col.prop(self, "outerRadius")
1328 elif self.ProfileType == 'Star':
1329 col.prop(self, "starPoints")
1330 col.prop(self, "starTwist")
1332 col = box.column(align=True)
1333 col.prop(self, "innerRadius")
1334 col.prop(self, "outerRadius")
1336 elif self.ProfileType == 'Arc':
1337 col.prop(self, "arcType")
1338 col.prop(self, "arcSides")
1340 col = box.column(align=True)
1341 col.prop(self, "startAngle")
1342 col.prop(self, "endAngle")
1344 col = box.column(align=True)
1345 col.prop(self, "innerRadius")
1346 col.prop(self, "outerRadius")
1348 elif self.ProfileType == 'Cogwheel':
1349 col.prop(self, "teeth")
1350 col.prop(self, "bevel")
1352 col = box.column(align=True)
1353 col.prop(self, "innerRadius")
1354 col.prop(self, "middleRadius")
1355 col.prop(self, "outerRadius")
1357 elif self.ProfileType == 'Nsided':
1358 col.prop(self, "Nsides")
1359 col.prop(self, "outerRadius")
1361 elif self.ProfileType == 'Splat':
1362 col.prop(self, "splatSides")
1363 col.prop(self, "outerRadius")
1365 col = box.column(align=True)
1366 col.prop(self, "splatScale")
1367 col.prop(self, "seed")
1368 col.prop(self, "basis")
1370 elif self.ProfileType == 'Cycloid':
1371 col.prop(self, "cycloType")
1372 col.prop(self, "cycloPoints")
1374 col = box.column(align=True)
1375 col.prop(self, "cyclo_a")
1376 col.prop(self, "cyclo_b")
1377 if self.cycloType != 0:
1378 col.prop(self, "cyclo_d")
1380 elif self.ProfileType == 'Helix':
1381 col.prop(self, "helixPoints")
1382 col.prop(self, "helixHeight")
1383 col.prop(self, "helixWidth")
1385 col = box.column(align=True)
1386 col.prop(self, "helixStart")
1387 col.prop(self, "helixEnd")
1389 col = box.column(align=True)
1390 col.prop(self, "helix_a")
1391 col.prop(self, "helix_b")
1393 elif self.ProfileType == 'Noise':
1394 col.prop(self, "noiseType")
1395 col.prop(self, "noisePoints")
1396 col.prop(self, "noiseLength")
1398 col = box.column(align=True)
1399 col.prop(self, "noiseSize")
1400 col.prop(self, "noiseScaleX")
1401 col.prop(self, "noiseScaleY")
1402 col.prop(self, "noiseScaleZ")
1404 col = box.column(align=True)
1405 col.prop(self, "noiseOctaves")
1406 col.prop(self, "noiseBasis")
1407 col.prop(self, "noiseSeed")
1409 row = layout.row()
1410 row.prop(self, "shape", expand=True)
1412 # output options
1413 col = layout.column()
1414 col.label(text="Output Curve Type:")
1415 col.row().prop(self, "outputType", expand=True)
1417 if self.outputType == 'NURBS':
1418 col.prop(self, 'order_u')
1419 elif self.outputType == 'BEZIER':
1420 col.row().prop(self, 'handleType', expand=True)
1422 col = layout.column()
1423 col.row().prop(self, "use_cyclic_u", expand=True)
1425 col = layout.column()
1426 col.row().prop(self, "edit_mode", expand=True)
1428 col = layout.column()
1429 # AddObjectHelper props
1430 col.prop(self, "align")
1431 col.prop(self, "location")
1432 col.prop(self, "rotation")
1434 @classmethod
1435 def poll(cls, context):
1436 return context.scene is not None
1438 def execute(self, context):
1439 # turn off 'Enter Edit Mode'
1440 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
1441 bpy.context.preferences.edit.use_enter_edit_mode = False
1443 # main function
1444 main(context, self)
1446 if use_enter_edit_mode:
1447 bpy.ops.object.mode_set(mode = 'EDIT')
1449 # restore pre operator state
1450 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
1452 if self.edit_mode:
1453 bpy.ops.object.mode_set(mode = 'EDIT')
1454 else:
1455 bpy.ops.object.mode_set(mode = 'OBJECT')
1457 return {'FINISHED'}
1459 def invoke(self, context, event):
1460 # deal with 2D - 3D curve differences
1461 if self.ProfileType in ['Helix', 'Cycloid', 'Noise']:
1462 self.shape = '3D'
1463 else:
1464 self.shape = '2D'
1466 if self.ProfileType in ['Helix', 'Noise', 'Cycloid']:
1467 self.use_cyclic_u = False
1468 if self.ProfileType in ['Cycloid']:
1469 if self.cycloType == 0:
1470 self.use_cyclic_u = False
1471 else:
1472 self.use_cyclic_u = True
1473 else:
1474 if self.ProfileType == 'Arc' and self.arcType == 1:
1475 self.use_cyclic_u = False
1476 else:
1477 self.use_cyclic_u = True
1479 self.execute(context)
1481 return {'FINISHED'}
1483 # Register
1484 classes = [
1485 Curveaceous_galore
1488 def register():
1489 from bpy.utils import register_class
1490 for cls in classes:
1491 register_class(cls)
1493 def unregister():
1494 from bpy.utils import unregister_class
1495 for cls in reversed(classes):
1496 unregister_class(cls)
1498 if __name__ == "__main__":
1499 register()