Import Images: bump minimum Blender version after API change
[blender-addons.git] / add_curve_extra_objects / add_curve_aceous_galore.py
blobad851dd1178d9e7e04c2c66c405a8b6d4ab61855
1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """
6 bl_info = {
7 "name": "Curveaceous Galore!",
8 "author": "Jimmy Hazevoet, testscreenings",
9 "version": (0, 2, 3),
10 "blender": (2, 80),
11 "location": "View3D > Add > Curve",
12 "description": "Adds many different types of Curves",
13 "warning": "",
14 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
15 "category": "Add Curve",
17 """
19 import bpy
20 from bpy_extras import object_utils
21 from bpy.props import (
22 BoolProperty,
23 EnumProperty,
24 FloatProperty,
25 IntProperty,
26 FloatVectorProperty
28 from mathutils import Matrix, Vector
29 from bpy.types import Operator
30 from math import (
31 sin, cos, pi
33 import mathutils.noise as Noise
36 # ------------------------------------------------------------
37 # Some functions to use with others:
38 # ------------------------------------------------------------
40 # ------------------------------------------------------------
41 # Generate random number:
42 def randnum(low=0.0, high=1.0, seed=0):
43 """
44 randnum( low=0.0, high=1.0, seed=0 )
46 Create random number
47 Parameters:
48 low - lower range
49 (type=float)
50 high - higher range
51 (type=float)
52 seed - the random seed number, if seed == 0, the current time will be used instead
53 (type=int)
54 Returns:
55 a random number
56 (type=float)
57 """
59 Noise.seed_set(seed)
60 rnum = Noise.random()
61 rnum = rnum * (high - low)
62 rnum = rnum + low
63 return rnum
66 # ------------------------------------------------------------
67 # Make some noise:
68 def vTurbNoise(x, y, z, iScale=0.25, Size=1.0, Depth=6, Hard=False, Basis=0, Seed=0):
69 """
70 vTurbNoise((x,y,z), iScale=0.25, Size=1.0, Depth=6, Hard=0, Basis=0, Seed=0 )
72 Create randomised vTurbulence noise
74 Parameters:
75 xyz - (x,y,z) float values.
76 (type=3-float tuple)
77 iScale - noise intensity scale
78 (type=float)
79 Size - noise size
80 (type=float)
81 Depth - number of noise values added.
82 (type=int)
83 Hard - noise hardness: True - soft noise; False - hard noise
84 (type=int)
85 basis - type of noise used for turbulence
86 (type=int)
87 Seed - the random seed number, if seed == 0, the current time will be used instead
88 (type=int)
89 Returns:
90 the generated turbulence vector.
91 (type=3-float list)
92 """
93 rand = randnum(-100, 100, Seed)
94 if Basis == 9:
95 Basis = 14
96 vec = Vector((x / Size + rand, y / Size + rand, z / Size + rand))
97 vTurb = Noise.turbulence_vector(vec, Depth, Hard)
98 #mathutils.noise.turbulence_vector(position, octaves, hard, noise_basis='PERLIN_ORIGINAL', amplitude_scale=0.5, frequency_scale=2.0)
99 tx = vTurb[0] * iScale
100 ty = vTurb[1] * iScale
101 tz = vTurb[2] * iScale
102 return tx, ty, tz
105 # -------------------------------------------------------------------
106 # 2D Curve shape functions:
107 # -------------------------------------------------------------------
109 # ------------------------------------------------------------
110 # 2DCurve: Profile: L, H, T, U, Z
111 def ProfileCurve(type=0, a=0.25, b=0.25):
113 ProfileCurve( type=0, a=0.25, b=0.25 )
115 Create profile curve
117 Parameters:
118 type - select profile type, L, H, T, U, Z
119 (type=int)
120 a - a scaling parameter
121 (type=float)
122 b - b scaling parameter
123 (type=float)
124 Returns:
125 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
126 (type=list)
129 newpoints = []
130 if type == 1:
131 # H:
132 a *= 0.5
133 b *= 0.5
134 newpoints = [
135 [-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], [1.0, -1.0, 0.0], [1.0 - a, -1.0, 0.0],
138 [1.0 - a, -b, 0.0], [-1.0 + a, -b, 0.0], [-1.0 + a, -1.0, 0.0],
139 [-1.0, -1.0, 0.0]
141 elif type == 2:
142 # T:
143 a *= 0.5
144 newpoints = [
145 [-1.0, 1.0, 0.0], [1.0, 1.0, 0.0],
146 [1.0, 1.0 - b, 0.0], [a, 1.0 - b, 0.0], [a, -1.0, 0.0],
147 [-a, -1.0, 0.0], [-a, 1.0 - b, 0.0], [-1.0, 1.0 - b, 0.0]
149 elif type == 3:
150 # U:
151 a *= 0.5
152 newpoints = [
153 [-1.0, 1.0, 0.0], [-1.0 + a, 1.0, 0.0],
154 [-1.0 + a, -1.0 + b, 0.0], [1.0 - a, -1.0 + b, 0.0], [1.0 - a, 1.0, 0.0],
155 [1.0, 1.0, 0.0], [1.0, -1.0, 0.0], [-1.0, -1.0, 0.0]
157 elif type == 4:
158 # Z:
159 a *= 0.5
160 newpoints = [
161 [-0.5, 1.0, 0.0], [a, 1.0, 0.0],
162 [a, -1.0 + b, 0.0], [1.0, -1.0 + b, 0.0], [1.0, -1.0, 0.0],
163 [-a, -1.0, 0.0], [-a, 1.0 - b, 0.0], [-1.0, 1.0 - b, 0.0],
164 [-1.0, 1.0, 0.0]
166 else:
167 # L:
168 newpoints = [
169 [-1.0, 1.0, 0.0], [-1.0 + a, 1.0, 0.0],
170 [-1.0 + a, -1.0 + b, 0.0], [1.0, -1.0 + b, 0.0],
171 [1.0, -1.0, 0.0], [-1.0, -1.0, 0.0]
173 return newpoints
176 # ------------------------------------------------------------
177 # 2DCurve: Arrow
178 def ArrowCurve(type=1, a=1.0, b=0.5):
180 ArrowCurve( type=1, a=1.0, b=0.5, c=1.0 )
182 Create arrow curve
184 Parameters:
185 type - select type, Arrow1, Arrow2
186 (type=int)
187 a - a scaling parameter
188 (type=float)
189 b - b scaling parameter
190 (type=float)
191 Returns:
192 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
193 (type=list)
196 newpoints = []
197 if type == 0:
198 # Arrow1:
199 a *= 0.5
200 b *= 0.5
201 newpoints = [
202 [-1.0, b, 0.0], [-1.0 + a, b, 0.0],
203 [-1.0 + a, 1.0, 0.0], [1.0, 0.0, 0.0],
204 [-1.0 + a, -1.0, 0.0], [-1.0 + a, -b, 0.0],
205 [-1.0, -b, 0.0]
207 elif type == 1:
208 # Arrow2:
209 newpoints = [[-a, b, 0.0], [a, 0.0, 0.0], [-a, -b, 0.0], [0.0, 0.0, 0.0]]
210 else:
211 # diamond:
212 newpoints = [[0.0, b, 0.0], [a, 0.0, 0.0], [0.0, -b, 0.0], [-a, 0.0, 0.0]]
213 return newpoints
216 # ------------------------------------------------------------
217 # 2DCurve: Square / Rectangle
218 def RectCurve(type=1, a=1.0, b=0.5, c=1.0):
220 RectCurve( type=1, a=1.0, b=0.5, c=1.0 )
222 Create square / rectangle curve
224 Parameters:
225 type - select type, Square, Rounded square 1, Rounded square 2
226 (type=int)
227 a - a scaling parameter
228 (type=float)
229 b - b scaling parameter
230 (type=float)
231 c - c scaling parameter
232 (type=float)
233 Returns:
234 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
235 (type=list)
238 newpoints = []
239 if type == 1:
240 # Rounded Rectangle:
241 newpoints = [
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],
244 [a, -b + b * 0.2, 0.0], [a - a * 0.05, -b + b * 0.05, 0.0], [a - a * 0.2, -b, 0.0],
245 [-a + a * 0.2, -b, 0.0], [-a + a * 0.05, -b + b * 0.05, 0.0], [-a, -b + b * 0.2, 0.0]
247 elif type == 2:
248 # Rounded Rectangle II:
249 newpoints = []
250 x = a
251 y = b
252 r = c
253 if r > x:
254 r = x - 0.0001
255 if r > y:
256 r = y - 0.0001
257 if r > 0:
258 newpoints.append([-x + r, y, 0])
259 newpoints.append([x - r, y, 0])
260 newpoints.append([x, y - r, 0])
261 newpoints.append([x, -y + r, 0])
262 newpoints.append([x - r, -y, 0])
263 newpoints.append([-x + r, -y, 0])
264 newpoints.append([-x, -y + r, 0])
265 newpoints.append([-x, y - r, 0])
266 else:
267 newpoints.append([-x, y, 0])
268 newpoints.append([x, y, 0])
269 newpoints.append([x, -y, 0])
270 newpoints.append([-x, -y, 0])
271 else:
272 # Rectangle:
273 newpoints = [[-a, b, 0.0], [a, b, 0.0], [a, -b, 0.0], [-a, -b, 0.0]]
274 return newpoints
277 # ------------------------------------------------------------
278 # 2DCurve: Star:
279 def StarCurve(starpoints=8, innerradius=0.5, outerradius=1.0, twist=0.0):
281 StarCurve( starpoints=8, innerradius=0.5, outerradius=1.0, twist=0.0 )
283 Create star shaped curve
285 Parameters:
286 starpoints - the number of points
287 (type=int)
288 innerradius - innerradius
289 (type=float)
290 outerradius - outerradius
291 (type=float)
292 twist - twist amount
293 (type=float)
294 Returns:
295 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
296 (type=list)
299 newpoints = []
300 step = 2.0 / starpoints
301 i = 0
302 while i < starpoints:
303 t = i * step
304 x1 = cos(t * pi) * outerradius
305 y1 = sin(t * pi) * outerradius
306 newpoints.append([x1, y1, 0])
307 x2 = cos(t * pi + (pi / starpoints + twist)) * innerradius
308 y2 = sin(t * pi + (pi / starpoints + twist)) * innerradius
309 newpoints.append([x2, y2, 0])
310 i += 1
311 return newpoints
314 # ------------------------------------------------------------
315 # 2DCurve: Flower:
316 def FlowerCurve(petals=8, innerradius=0.5, outerradius=1.0, petalwidth=2.0):
318 FlowerCurve( petals=8, innerradius=0.5, outerradius=1.0, petalwidth=2.0 )
320 Create flower shaped curve
322 Parameters:
323 petals - the number of petals
324 (type=int)
325 innerradius - innerradius
326 (type=float)
327 outerradius - outerradius
328 (type=float)
329 petalwidth - width of petals
330 (type=float)
331 Returns:
332 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
333 (type=list)
336 newpoints = []
337 step = 2.0 / petals
338 pet = (step / pi * 2) * petalwidth
339 i = 0
340 while i < petals:
341 t = i * step
342 x1 = cos(t * pi - (pi / petals)) * innerradius
343 y1 = sin(t * pi - (pi / petals)) * innerradius
344 newpoints.append([x1, y1, 0])
345 x2 = cos(t * pi - pet) * outerradius
346 y2 = sin(t * pi - pet) * outerradius
347 newpoints.append([x2, y2, 0])
348 x3 = cos(t * pi + pet) * outerradius
349 y3 = sin(t * pi + pet) * outerradius
350 newpoints.append([x3, y3, 0])
351 i += 1
352 return newpoints
355 # ------------------------------------------------------------
356 # 2DCurve: Arc,Sector,Segment,Ring:
357 def ArcCurve(sides=6, startangle=0.0, endangle=90.0, innerradius=0.5, outerradius=1.0, type=3):
359 ArcCurve( sides=6, startangle=0.0, endangle=90.0, innerradius=0.5, outerradius=1.0, type=3 )
361 Create arc shaped curve
363 Parameters:
364 sides - number of sides
365 (type=int)
366 startangle - startangle
367 (type=float)
368 endangle - endangle
369 (type=float)
370 innerradius - innerradius
371 (type=float)
372 outerradius - outerradius
373 (type=float)
374 type - select type Arc,Sector,Segment,Ring
375 (type=int)
376 Returns:
377 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
378 (type=list)
381 newpoints = []
382 sides += 1
383 angle = 2.0 * (1.0 / 360.0)
384 endangle -= startangle
385 step = (angle * endangle) / (sides - 1)
386 i = 0
387 while i < sides:
388 t = (i * step) + angle * startangle
389 x1 = sin(t * pi) * outerradius
390 y1 = cos(t * pi) * outerradius
391 newpoints.append([x1, y1, 0])
392 i += 1
394 # if type == 1:
395 # Arc: turn cyclic curve flag off!
397 # Segment:
398 if type == 2:
399 newpoints.append([0, 0, 0])
400 # Ring:
401 elif type == 3:
402 j = sides - 1
403 while j > -1:
404 t = (j * step) + angle * startangle
405 x2 = sin(t * pi) * innerradius
406 y2 = cos(t * pi) * innerradius
407 newpoints.append([x2, y2, 0])
408 j -= 1
409 return newpoints
412 # ------------------------------------------------------------
413 # 2DCurve: Cog wheel:
414 def CogCurve(theeth=8, innerradius=0.8, middleradius=0.95, outerradius=1.0, bevel=0.5):
416 CogCurve( theeth=8, innerradius=0.8, middleradius=0.95, outerradius=1.0, bevel=0.5 )
418 Create cog wheel shaped curve
420 Parameters:
421 theeth - number of theeth
422 (type=int)
423 innerradius - innerradius
424 (type=float)
425 middleradius - middleradius
426 (type=float)
427 outerradius - outerradius
428 (type=float)
429 bevel - bevel amount
430 (type=float)
431 Returns:
432 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
433 (type=list)
436 newpoints = []
437 step = 2.0 / theeth
438 pet = step / pi * 2
439 bevel = 1.0 - bevel
440 i = 0
441 while i < theeth:
442 t = i * step
443 x1 = cos(t * pi - (pi / theeth) - pet) * innerradius
444 y1 = sin(t * pi - (pi / theeth) - pet) * innerradius
445 newpoints.append([x1, y1, 0])
446 x2 = cos(t * pi - (pi / theeth) + pet) * innerradius
447 y2 = sin(t * pi - (pi / theeth) + pet) * innerradius
448 newpoints.append([x2, y2, 0])
449 x3 = cos(t * pi - pet) * middleradius
450 y3 = sin(t * pi - pet) * middleradius
451 newpoints.append([x3, y3, 0])
452 x4 = cos(t * pi - (pet * bevel)) * outerradius
453 y4 = sin(t * pi - (pet * bevel)) * outerradius
454 newpoints.append([x4, y4, 0])
455 x5 = cos(t * pi + (pet * bevel)) * outerradius
456 y5 = sin(t * pi + (pet * bevel)) * outerradius
457 newpoints.append([x5, y5, 0])
458 x6 = cos(t * pi + pet) * middleradius
459 y6 = sin(t * pi + pet) * middleradius
460 newpoints.append([x6, y6, 0])
461 i += 1
462 return newpoints
465 # ------------------------------------------------------------
466 # 2DCurve: nSide:
467 def nSideCurve(sides=6, radius=1.0):
469 nSideCurve( sides=6, radius=1.0 )
471 Create n-sided curve
473 Parameters:
474 sides - number of sides
475 (type=int)
476 radius - radius
477 (type=float)
478 Returns:
479 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
480 (type=list)
483 newpoints = []
484 step = 2.0 / sides
485 i = 0
486 while i < sides:
487 t = i * step
488 x = sin(t * pi) * radius
489 y = cos(t * pi) * radius
490 newpoints.append([x, y, 0])
491 i += 1
492 return newpoints
495 # ------------------------------------------------------------
496 # 2DCurve: Splat:
497 def SplatCurve(sides=24, scale=1.0, seed=0, basis=0, radius=1.0):
499 SplatCurve( sides=24, scale=1.0, seed=0, basis=0, radius=1.0 )
501 Create splat curve
503 Parameters:
504 sides - number of sides
505 (type=int)
506 scale - noise size
507 (type=float)
508 seed - noise random seed
509 (type=int)
510 basis - noise basis
511 (type=int)
512 radius - radius
513 (type=float)
514 Returns:
515 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
516 (type=list)
519 newpoints = []
520 step = 2.0 / sides
521 i = 0
522 while i < sides:
523 t = i * step
524 turb = vTurbNoise(t, t, t, 1.0, scale, 6, False, basis, seed)
525 turb = turb[2] * 0.5 + 0.5
526 x = sin(t * pi) * radius * turb
527 y = cos(t * pi) * radius * turb
528 newpoints.append([x, y, 0])
529 i += 1
530 return newpoints
533 # -----------------------------------------------------------
534 # Cycloid curve
535 def CycloidCurve(number=100, type=0, R=4.0, r=1.0, d=1.0):
537 CycloidCurve( number=100, type=0, a=4.0, b=1.0 )
539 Create a Cycloid, Hypotrochoid / Hypocycloid or Epitrochoid / Epycycloid type of curve
541 Parameters:
542 number - the number of points
543 (type=int)
544 type - types: Cycloid, Hypocycloid, Epicycloid
545 (type=int)
546 R = Radius a scaling parameter
547 (type=float)
548 r = Radius b scaling parameter
549 (type=float)
550 d = Distance scaling parameter
551 (type=float)
552 Returns:
553 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
554 (type=list)
557 a = R
558 b = r
559 newpoints = []
560 step = 2.0 / (number - 1)
561 i = 0
562 if type == 1:
563 # Hypotrochoid / Hypocycloid
564 while i < number:
565 t = i * step
566 x = ((a - b) * cos(t * pi)) + (d * cos(((a + b) / b) * t * pi))
567 y = ((a - b) * sin(t * pi)) - (d * sin(((a + b) / b) * t * pi))
568 z = 0
569 newpoints.append([x, y, z])
570 i += 1
571 elif type == 2:
572 # Epitrochoid / Epycycloid
573 while i < number:
574 t = i * step
575 x = ((a + b) * cos(t * pi)) - (d * cos(((a + b) / b) * t * pi))
576 y = ((a + b) * sin(t * pi)) - (d * sin(((a + b) / b) * t * pi))
577 z = 0
578 newpoints.append([x, y, z])
579 i += 1
580 else:
581 # Cycloid
582 while i < number:
583 t = (i * step * pi)
584 x = (t - sin(t) * b) * a / pi
585 y = (1 - cos(t) * b) * a / pi
586 z = 0
587 newpoints.append([x, y, z])
588 i += 1
589 return newpoints
592 # -----------------------------------------------------------
593 # 3D curve shape functions:
594 # -----------------------------------------------------------
596 # ------------------------------------------------------------
597 # 3DCurve: Helix:
598 def HelixCurve(number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0):
600 HelixCurve( number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0 )
602 Create helix curve
604 Parameters:
605 number - the number of points
606 (type=int)
607 height - height
608 (type=float)
609 startangle - startangle
610 (type=float)
611 endangle - endangle
612 (type=float)
613 width - width
614 (type=float)
615 a - a
616 (type=float)
617 b - b
618 (type=float)
619 Returns:
620 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
621 (type=list)
624 newpoints = []
625 angle = (2.0 / 360.0) * (endangle - startangle)
626 step = angle / (number - 1)
627 h = height / angle
628 start = startangle * 2.0 / 360.0
629 a /= angle
630 i = 0
631 while i < number:
632 t = (i * step + start)
633 x = sin((t * pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width)
634 y = cos((t * pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width)
635 z = (t * h) - h * start
636 newpoints.append([x, y, z])
637 i += 1
638 return newpoints
641 # -----------------------------------------------------------
642 # 3D Noise curve
643 def NoiseCurve(type=0, number=100, length=2.0, size=0.5,
644 scale=[0.5, 0.5, 0.5], octaves=2, basis=0, seed=0):
646 Create noise curve
648 Parameters:
649 number - number of points
650 (type=int)
651 length - curve length
652 (type=float)
653 size - noise size
654 (type=float)
655 scale - noise intensity scale x,y,z
656 (type=list)
657 basis - noise basis
658 (type=int)
659 seed - noise random seed
660 (type=int)
661 type - noise curve type
662 (type=int)
663 Returns:
664 a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n]
665 (type=list)
668 newpoints = []
669 step = (length / number)
670 i = 0
671 if type == 1:
672 # noise circle
673 while i < number:
674 t = i * step
675 v = vTurbNoise(t, t, t, 1.0, size, octaves, False, basis, seed)
676 x = sin(t * pi) + (v[0] * scale[0])
677 y = cos(t * pi) + (v[1] * scale[1])
678 z = v[2] * scale[2]
679 newpoints.append([x, y, z])
680 i += 1
681 elif type == 2:
682 # noise knot / ball
683 while i < number:
684 t = i * step
685 v = vTurbNoise(t, t, t, 1.0, 1.0, octaves, False, basis, seed)
686 x = v[0] * scale[0] * size
687 y = v[1] * scale[1] * size
688 z = v[2] * scale[2] * size
689 newpoints.append([x, y, z])
690 i += 1
691 else:
692 # noise linear
693 while i < number:
694 t = i * step
695 v = vTurbNoise(t, t, t, 1.0, size, octaves, False, basis, seed)
696 x = t + v[0] * scale[0]
697 y = v[1] * scale[1]
698 z = v[2] * scale[2]
699 newpoints.append([x, y, z])
700 i += 1
701 return newpoints
704 # get array of vertcoordinates according to splinetype
705 def vertsToPoints(Verts, splineType):
707 # main vars
708 vertArray = []
710 # array for BEZIER spline output (V3)
711 if splineType == 'BEZIER':
712 for v in Verts:
713 vertArray += v
715 # array for nonBEZIER output (V4)
716 else:
717 for v in Verts:
718 vertArray += v
719 if splineType == 'NURBS':
720 # for nurbs w=1
721 vertArray.append(1)
722 else:
723 # for poly w=0
724 vertArray.append(0)
725 return vertArray
728 # create new CurveObject from vertarray and splineType
729 def createCurve(context, vertArray, self):
730 # output splineType 'POLY' 'NURBS' 'BEZIER'
731 splineType = self.outputType
733 # GalloreType as name
734 name = self.ProfileType
736 # create object
737 if bpy.context.mode == 'EDIT_CURVE':
738 Curve = context.active_object
739 newSpline = Curve.data.splines.new(type=splineType) # spline
740 else:
741 # create curve
742 dataCurve = bpy.data.curves.new(name, type='CURVE') # curve data block
743 newSpline = dataCurve.splines.new(type=splineType) # spline
745 # create object with newCurve
746 Curve = object_utils.object_data_add(context, dataCurve, operator=self) # place in active scene
748 # set newSpline Options
749 newSpline.use_cyclic_u = self.use_cyclic_u
750 newSpline.use_endpoint_u = self.endp_u
751 newSpline.order_u = self.order_u
753 # set curve Options
754 Curve.data.dimensions = self.shape
755 Curve.data.use_path = True
756 if self.shape == '3D':
757 Curve.data.fill_mode = 'FULL'
758 else:
759 Curve.data.fill_mode = 'BOTH'
761 for spline in Curve.data.splines:
762 if spline.type == 'BEZIER':
763 for point in spline.bezier_points:
764 point.select_control_point = False
765 point.select_left_handle = False
766 point.select_right_handle = False
767 else:
768 for point in spline.points:
769 point.select = False
771 # create spline from vertarray
772 if splineType == 'BEZIER':
773 newSpline.bezier_points.add(int(len(vertArray) * 0.33))
774 newSpline.bezier_points.foreach_set('co', vertArray)
775 for point in newSpline.bezier_points:
776 point.handle_right_type = self.handleType
777 point.handle_left_type = self.handleType
778 point.select_control_point = True
779 point.select_left_handle = True
780 point.select_right_handle = True
781 else:
782 newSpline.points.add(int(len(vertArray) * 0.25 - 1))
783 newSpline.points.foreach_set('co', vertArray)
784 newSpline.use_endpoint_u = True
785 for point in newSpline.points:
786 point.select = True
788 # move and rotate spline in edit mode
789 if bpy.context.mode == 'EDIT_CURVE':
790 if self.align == "WORLD":
791 location = self.location - context.active_object.location
792 bpy.ops.transform.translate(value = location, orient_type='GLOBAL')
793 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X', orient_type='GLOBAL')
794 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y', orient_type='GLOBAL')
795 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z', orient_type='GLOBAL')
797 elif self.align == "VIEW":
798 bpy.ops.transform.translate(value = self.location)
799 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
800 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
801 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
803 elif self.align == "CURSOR":
804 location = context.active_object.location
805 self.location = bpy.context.scene.cursor.location - location
806 self.rotation = bpy.context.scene.cursor.rotation_euler
808 bpy.ops.transform.translate(value = self.location)
809 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
810 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
811 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
813 return
816 # ------------------------------------------------------------
817 # Main Function
818 def main(context, self):
819 # options
820 proType = self.ProfileType
821 splineType = self.outputType
822 innerRadius = self.innerRadius
823 middleRadius = self.middleRadius
824 outerRadius = self.outerRadius
826 # get verts
827 if proType == 'Profile':
828 verts = ProfileCurve(
829 self.ProfileCurveType,
830 self.ProfileCurvevar1,
831 self.ProfileCurvevar2
833 if proType == 'Arrow':
834 verts = ArrowCurve(
835 self.MiscCurveType,
836 self.MiscCurvevar1,
837 self.MiscCurvevar2
839 if proType == 'Rectangle':
840 verts = RectCurve(
841 self.MiscCurveType,
842 self.MiscCurvevar1,
843 self.MiscCurvevar2,
844 self.MiscCurvevar3
846 if proType == 'Flower':
847 verts = FlowerCurve(
848 self.petals,
849 innerRadius,
850 outerRadius,
851 self.petalWidth
853 if proType == 'Star':
854 verts = StarCurve(
855 self.starPoints,
856 innerRadius,
857 outerRadius,
858 self.starTwist
860 if proType == 'Arc':
861 verts = ArcCurve(
862 self.arcSides,
863 self.startAngle,
864 self.endAngle,
865 innerRadius,
866 outerRadius,
867 self.arcType
869 if proType == 'Cogwheel':
870 verts = CogCurve(
871 self.teeth,
872 innerRadius,
873 middleRadius,
874 outerRadius,
875 self.bevel
877 if proType == 'Nsided':
878 verts = nSideCurve(
879 self.Nsides,
880 outerRadius
882 if proType == 'Splat':
883 verts = SplatCurve(
884 self.splatSides,
885 self.splatScale,
886 self.seed,
887 self.basis,
888 outerRadius
890 if proType == 'Cycloid':
891 verts = CycloidCurve(
892 self.cycloPoints,
893 self.cycloType,
894 self.cyclo_a,
895 self.cyclo_b,
896 self.cyclo_d
898 if proType == 'Helix':
899 verts = HelixCurve(
900 self.helixPoints,
901 self.helixHeight,
902 self.helixStart,
903 self.helixEnd,
904 self.helixWidth,
905 self.helix_a,
906 self.helix_b
908 if proType == 'Noise':
909 verts = NoiseCurve(
910 self.noiseType,
911 self.noisePoints,
912 self.noiseLength,
913 self.noiseSize,
914 [self.noiseScaleX, self.noiseScaleY, self.noiseScaleZ],
915 self.noiseOctaves,
916 self.noiseBasis,
917 self.noiseSeed
920 # turn verts into array
921 vertArray = vertsToPoints(verts, splineType)
923 # create object
924 createCurve(context, vertArray, self)
926 return
929 class Curveaceous_galore(Operator, object_utils.AddObjectHelper):
930 bl_idname = "curve.curveaceous_galore"
931 bl_label = "Curve Profiles"
932 bl_description = "Construct many types of curves"
933 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
935 # general properties
936 ProfileType : EnumProperty(
937 name="Type",
938 description="Form of Curve to create",
939 items=[
940 ('Arc', "Arc", "Arc"),
941 ('Arrow', "Arrow", "Arrow"),
942 ('Cogwheel', "Cogwheel", "Cogwheel"),
943 ('Cycloid', "Cycloid", "Cycloid"),
944 ('Flower', "Flower", "Flower"),
945 ('Helix', "Helix (3D)", "Helix"),
946 ('Noise', "Noise (3D)", "Noise"),
947 ('Nsided', "Nsided", "Nsided"),
948 ('Profile', "Profile", "Profile"),
949 ('Rectangle', "Rectangle", "Rectangle"),
950 ('Splat', "Splat", "Splat"),
951 ('Star', "Star", "Star")]
953 outputType : EnumProperty(
954 name="Output splines",
955 description="Type of splines to output",
956 items=[
957 ('POLY', "Poly", "Poly Spline type"),
958 ('NURBS', "Nurbs", "Nurbs Spline type"),
959 ('BEZIER', "Bezier", "Bezier Spline type")]
961 # Curve Options
962 shape : EnumProperty(
963 name="2D / 3D",
964 description="2D or 3D Curve",
965 items=[
966 ('2D', "2D", "2D"),
967 ('3D', "3D", "3D")
970 use_cyclic_u : BoolProperty(
971 name="Cyclic",
972 default=True,
973 description="make curve closed"
975 endp_u : BoolProperty(
976 name="Use endpoint u",
977 default=True,
978 description="stretch to endpoints"
980 order_u : IntProperty(
981 name="Order u",
982 default=4,
983 min=2, soft_min=2,
984 max=6, soft_max=6,
985 description="Order of nurbs spline"
987 handleType : EnumProperty(
988 name="Handle type",
989 default='AUTO',
990 description="Bezier handles type",
991 items=[
992 ('VECTOR', "Vector", "Vector type Bezier handles"),
993 ('AUTO', "Auto", "Automatic type Bezier handles")]
995 # ProfileCurve properties
996 ProfileCurveType : IntProperty(
997 name="Type",
998 min=1,
999 max=5,
1000 default=1,
1001 description="Type of Curve's Profile"
1003 ProfileCurvevar1 : FloatProperty(
1004 name="Variable 1",
1005 default=0.25,
1006 description="Variable 1 of Curve's Profile"
1008 ProfileCurvevar2 : FloatProperty(
1009 name="Variable 2",
1010 default=0.25,
1011 description="Variable 2 of Curve's Profile"
1013 # Arrow, Rectangle, MiscCurve properties
1014 MiscCurveType : IntProperty(
1015 name="Type",
1016 min=0,
1017 max=3,
1018 default=0,
1019 description="Type of Curve"
1021 MiscCurvevar1 : FloatProperty(
1022 name="Variable 1",
1023 default=1.0,
1024 description="Variable 1 of Curve"
1026 MiscCurvevar2 : FloatProperty(
1027 name="Variable 2",
1028 default=0.5,
1029 description="Variable 2 of Curve"
1031 MiscCurvevar3 : FloatProperty(
1032 name="Variable 3",
1033 default=0.1,
1034 min=0,
1035 description="Variable 3 of Curve"
1037 # Common properties
1038 innerRadius : FloatProperty(
1039 name="Inner radius",
1040 default=0.5,
1041 min=0,
1042 description="Inner radius"
1044 middleRadius : FloatProperty(
1045 name="Middle radius",
1046 default=0.95,
1047 min=0,
1048 description="Middle radius"
1050 outerRadius : FloatProperty(
1051 name="Outer radius",
1052 default=1.0,
1053 min=0,
1054 description="Outer radius"
1056 # Flower properties
1057 petals : IntProperty(
1058 name="Petals",
1059 default=8,
1060 min=2,
1061 description="Number of petals"
1063 petalWidth : FloatProperty(
1064 name="Petal width",
1065 default=2.0,
1066 min=0.01,
1067 description="Petal width"
1069 # Star properties
1070 starPoints : IntProperty(
1071 name="Star points",
1072 default=8,
1073 min=2,
1074 description="Number of star points"
1076 starTwist : FloatProperty(
1077 name="Twist",
1078 default=0.0,
1079 description="Twist"
1081 # Arc properties
1082 arcSides : IntProperty(
1083 name="Arc sides",
1084 default=6,
1085 min=1,
1086 description="Sides of arc"
1088 startAngle : FloatProperty(
1089 name="Start angle",
1090 default=0.0,
1091 description="Start angle"
1093 endAngle : FloatProperty(
1094 name="End angle",
1095 default=90.0,
1096 description="End angle"
1098 arcType : IntProperty(
1099 name="Arc type",
1100 default=3,
1101 min=1,
1102 max=3,
1103 description="Sides of arc"
1105 # Cogwheel properties
1106 teeth : IntProperty(
1107 name="Teeth",
1108 default=8,
1109 min=2,
1110 description="number of teeth"
1112 bevel : FloatProperty(
1113 name="Bevel",
1114 default=0.5,
1115 min=0,
1116 max=1,
1117 description="Bevel"
1119 # Nsided property
1120 Nsides : IntProperty(
1121 name="Sides",
1122 default=8,
1123 min=3,
1124 description="Number of sides"
1126 # Splat properties
1127 splatSides : IntProperty(
1128 name="Splat sides",
1129 default=24,
1130 min=3,
1131 description="Splat sides"
1133 splatScale : FloatProperty(
1134 name="Splat scale",
1135 default=1.0,
1136 min=0.0001,
1137 description="Splat scale"
1139 seed : IntProperty(
1140 name="Seed",
1141 default=0,
1142 min=0,
1143 description="Seed"
1145 basis : IntProperty(
1146 name="Basis",
1147 default=0,
1148 min=0,
1149 max=14,
1150 description="Basis"
1152 # Helix properties
1153 helixPoints : IntProperty(
1154 name="Resolution",
1155 default=100,
1156 min=3,
1157 description="Resolution"
1159 helixHeight : FloatProperty(
1160 name="Height",
1161 default=2.0,
1162 min=0,
1163 description="Helix height"
1165 helixStart : FloatProperty(
1166 name="Start angle",
1167 default=0.0,
1168 description="Helix start angle"
1170 helixEnd : FloatProperty(
1171 name="Endangle",
1172 default=360.0,
1173 description="Helix end angle"
1175 helixWidth : FloatProperty(
1176 name="Width",
1177 default=1.0,
1178 description="Helix width"
1180 helix_a : FloatProperty(
1181 name="Variable 1",
1182 default=0.0,
1183 description="Helix Variable 1"
1185 helix_b : FloatProperty(
1186 name="Variable 2",
1187 default=0.0,
1188 description="Helix Variable 2"
1190 # Cycloid properties
1191 cycloPoints : IntProperty(
1192 name="Resolution",
1193 default=100,
1194 min=3,
1195 soft_min=3,
1196 description="Resolution"
1198 cycloType : IntProperty(
1199 name="Type",
1200 default=1,
1201 min=0,
1202 max=2,
1203 description="Type: Cycloid , Hypocycloid / Hypotrochoid , Epicycloid / Epitrochoid"
1205 cyclo_a : FloatProperty(
1206 name="R",
1207 default=1.0,
1208 min=0.01,
1209 description="Cycloid: R radius a"
1211 cyclo_b : FloatProperty(
1212 name="r",
1213 default=0.25,
1214 min=0.01,
1215 description="Cycloid: r radius b"
1217 cyclo_d : FloatProperty(
1218 name="d",
1219 default=0.25,
1220 description="Cycloid: d distance"
1222 # Noise properties
1223 noiseType : IntProperty(
1224 name="Type",
1225 default=0,
1226 min=0,
1227 max=2,
1228 description="Noise curve type: Linear, Circular or Knot"
1230 noisePoints : IntProperty(
1231 name="Resolution",
1232 default=100,
1233 min=3,
1234 description="Resolution"
1236 noiseLength : FloatProperty(
1237 name="Length",
1238 default=2.0,
1239 min=0.01,
1240 description="Curve Length"
1242 noiseSize : FloatProperty(
1243 name="Noise size",
1244 default=1.0,
1245 min=0.0001,
1246 description="Noise size"
1248 noiseScaleX : FloatProperty(
1249 name="Noise x",
1250 default=1.0,
1251 min=0.0001,
1252 description="Noise x"
1254 noiseScaleY : FloatProperty(
1255 name="Noise y",
1256 default=1.0,
1257 min=0.0001,
1258 description="Noise y"
1260 noiseScaleZ : FloatProperty(
1261 name="Noise z",
1262 default=1.0,
1263 min=0.0001,
1264 description="Noise z"
1266 noiseOctaves : IntProperty(
1267 name="Octaves",
1268 default=2,
1269 min=1,
1270 max=16,
1271 description="Basis"
1273 noiseBasis : IntProperty(
1274 name="Basis",
1275 default=0,
1276 min=0,
1277 max=9,
1278 description="Basis"
1280 noiseSeed : IntProperty(
1281 name="Seed",
1282 default=1,
1283 min=0,
1284 description="Random Seed"
1287 edit_mode : BoolProperty(
1288 name="Show in edit mode",
1289 default=True,
1290 description="Show in edit mode"
1293 def draw(self, context):
1294 layout = self.layout
1296 # general options
1297 col = layout.column()
1298 col.prop(self, 'ProfileType')
1299 col.label(text=self.ProfileType + " Options:")
1301 # options per ProfileType
1302 box = layout.box()
1303 col = box.column(align=True)
1305 if self.ProfileType == 'Profile':
1306 col.prop(self, "ProfileCurveType")
1307 col.prop(self, "ProfileCurvevar1")
1308 col.prop(self, "ProfileCurvevar2")
1310 elif self.ProfileType == 'Arrow':
1311 col.prop(self, "MiscCurveType")
1312 col.prop(self, "MiscCurvevar1", text="Height")
1313 col.prop(self, "MiscCurvevar2", text="Width")
1315 elif self.ProfileType == 'Rectangle':
1316 col.prop(self, "MiscCurveType")
1317 col.prop(self, "MiscCurvevar1", text="Width")
1318 col.prop(self, "MiscCurvevar2", text="Height")
1319 if self.MiscCurveType == 2:
1320 col.prop(self, "MiscCurvevar3", text="Corners")
1322 elif self.ProfileType == 'Flower':
1323 col.prop(self, "petals")
1324 col.prop(self, "petalWidth")
1326 col = box.column(align=True)
1327 col.prop(self, "innerRadius")
1328 col.prop(self, "outerRadius")
1330 elif self.ProfileType == 'Star':
1331 col.prop(self, "starPoints")
1332 col.prop(self, "starTwist")
1334 col = box.column(align=True)
1335 col.prop(self, "innerRadius")
1336 col.prop(self, "outerRadius")
1338 elif self.ProfileType == 'Arc':
1339 col.prop(self, "arcType")
1340 col.prop(self, "arcSides")
1342 col = box.column(align=True)
1343 col.prop(self, "startAngle")
1344 col.prop(self, "endAngle")
1346 col = box.column(align=True)
1347 col.prop(self, "innerRadius")
1348 col.prop(self, "outerRadius")
1350 elif self.ProfileType == 'Cogwheel':
1351 col.prop(self, "teeth")
1352 col.prop(self, "bevel")
1354 col = box.column(align=True)
1355 col.prop(self, "innerRadius")
1356 col.prop(self, "middleRadius")
1357 col.prop(self, "outerRadius")
1359 elif self.ProfileType == 'Nsided':
1360 col.prop(self, "Nsides")
1361 col.prop(self, "outerRadius")
1363 elif self.ProfileType == 'Splat':
1364 col.prop(self, "splatSides")
1365 col.prop(self, "outerRadius")
1367 col = box.column(align=True)
1368 col.prop(self, "splatScale")
1369 col.prop(self, "seed")
1370 col.prop(self, "basis")
1372 elif self.ProfileType == 'Cycloid':
1373 col.prop(self, "cycloType")
1374 col.prop(self, "cycloPoints")
1376 col = box.column(align=True)
1377 col.prop(self, "cyclo_a")
1378 col.prop(self, "cyclo_b")
1379 if self.cycloType != 0:
1380 col.prop(self, "cyclo_d")
1382 elif self.ProfileType == 'Helix':
1383 col.prop(self, "helixPoints")
1384 col.prop(self, "helixHeight")
1385 col.prop(self, "helixWidth")
1387 col = box.column(align=True)
1388 col.prop(self, "helixStart")
1389 col.prop(self, "helixEnd")
1391 col = box.column(align=True)
1392 col.prop(self, "helix_a")
1393 col.prop(self, "helix_b")
1395 elif self.ProfileType == 'Noise':
1396 col.prop(self, "noiseType")
1397 col.prop(self, "noisePoints")
1398 col.prop(self, "noiseLength")
1400 col = box.column(align=True)
1401 col.prop(self, "noiseSize")
1402 col.prop(self, "noiseScaleX")
1403 col.prop(self, "noiseScaleY")
1404 col.prop(self, "noiseScaleZ")
1406 col = box.column(align=True)
1407 col.prop(self, "noiseOctaves")
1408 col.prop(self, "noiseBasis")
1409 col.prop(self, "noiseSeed")
1411 row = layout.row()
1412 row.prop(self, "shape", expand=True)
1414 # output options
1415 col = layout.column()
1416 col.label(text="Output Curve Type:")
1417 col.row().prop(self, "outputType", expand=True)
1419 if self.outputType == 'NURBS':
1420 col.prop(self, 'order_u')
1421 elif self.outputType == 'BEZIER':
1422 col.row().prop(self, 'handleType', expand=True)
1424 col = layout.column()
1425 col.row().prop(self, "use_cyclic_u", expand=True)
1427 col = layout.column()
1428 col.row().prop(self, "edit_mode", expand=True)
1430 col = layout.column()
1431 # AddObjectHelper props
1432 col.prop(self, "align")
1433 col.prop(self, "location")
1434 col.prop(self, "rotation")
1436 @classmethod
1437 def poll(cls, context):
1438 return context.scene is not None
1440 def execute(self, context):
1441 # turn off 'Enter Edit Mode'
1442 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
1443 bpy.context.preferences.edit.use_enter_edit_mode = False
1445 # main function
1446 main(context, self)
1448 if use_enter_edit_mode:
1449 bpy.ops.object.mode_set(mode = 'EDIT')
1451 # restore pre operator state
1452 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
1454 if self.edit_mode:
1455 bpy.ops.object.mode_set(mode = 'EDIT')
1456 else:
1457 bpy.ops.object.mode_set(mode = 'OBJECT')
1459 return {'FINISHED'}
1461 def invoke(self, context, event):
1462 # deal with 2D - 3D curve differences
1463 if self.ProfileType in ['Helix', 'Cycloid', 'Noise']:
1464 self.shape = '3D'
1465 else:
1466 self.shape = '2D'
1468 if self.ProfileType in ['Helix', 'Noise', 'Cycloid']:
1469 self.use_cyclic_u = False
1470 if self.ProfileType in ['Cycloid']:
1471 if self.cycloType == 0:
1472 self.use_cyclic_u = False
1473 else:
1474 self.use_cyclic_u = True
1475 else:
1476 if self.ProfileType == 'Arc' and self.arcType == 1:
1477 self.use_cyclic_u = False
1478 else:
1479 self.use_cyclic_u = True
1481 self.execute(context)
1483 return {'FINISHED'}
1485 # Register
1486 classes = [
1487 Curveaceous_galore
1490 def register():
1491 from bpy.utils import register_class
1492 for cls in classes:
1493 register_class(cls)
1495 def unregister():
1496 from bpy.utils import unregister_class
1497 for cls in reversed(classes):
1498 unregister_class(cls)
1500 if __name__ == "__main__":
1501 register()