Merge branch 'blender-v3.6-release'
[blender-addons.git] / add_curve_extra_objects / add_curve_simple.py
blob7b6ed6edadd778217a6a122aadaa1de6af017b82
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "Simple Curve",
5 "author": "Vladimir Spivak (cwolf3d)",
6 "version": (1, 6, 1),
7 "blender": (2, 80, 0),
8 "location": "View3D > Add > Curve",
9 "description": "Adds Simple Curve",
10 "warning": "",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
12 "category": "Add Curve",
16 # ------------------------------------------------------------
18 import bpy
19 from bpy_extras import object_utils
20 from bpy.types import (
21 Operator,
22 Menu,
23 Panel,
24 PropertyGroup,
26 from bpy.props import (
27 BoolProperty,
28 EnumProperty,
29 FloatProperty,
30 FloatVectorProperty,
31 IntProperty,
32 StringProperty,
33 PointerProperty,
35 from mathutils import (
36 Vector,
37 Matrix,
39 from math import (
40 sin, asin, sqrt,
41 acos, cos, pi,
42 radians, tan,
43 hypot,
45 # from bpy_extras.object_utils import *
48 # ------------------------------------------------------------
49 # Point:
51 def SimplePoint():
52 newpoints = []
54 newpoints.append([0.0, 0.0, 0.0])
56 return newpoints
59 # ------------------------------------------------------------
60 # Line:
62 def SimpleLine(c1=[0.0, 0.0, 0.0], c2=[2.0, 2.0, 2.0]):
63 newpoints = []
65 c3 = Vector(c2) - Vector(c1)
66 newpoints.append([0.0, 0.0, 0.0])
67 newpoints.append([c3[0], c3[1], c3[2]])
69 return newpoints
72 # ------------------------------------------------------------
73 # Angle:
75 def SimpleAngle(length=1.0, angle=45.0):
76 newpoints = []
78 angle = radians(angle)
79 newpoints.append([length, 0.0, 0.0])
80 newpoints.append([0.0, 0.0, 0.0])
81 newpoints.append([length * cos(angle), length * sin(angle), 0.0])
83 return newpoints
86 # ------------------------------------------------------------
87 # Distance:
89 def SimpleDistance(length=1.0, center=True):
90 newpoints = []
92 if center:
93 newpoints.append([-length / 2, 0.0, 0.0])
94 newpoints.append([length / 2, 0.0, 0.0])
95 else:
96 newpoints.append([0.0, 0.0, 0.0])
97 newpoints.append([length, 0.0, 0.0])
99 return newpoints
102 # ------------------------------------------------------------
103 # Circle:
105 def SimpleCircle(sides=4, radius=1.0):
106 newpoints = []
108 angle = radians(360) / sides
109 newpoints.append([radius, 0, 0])
110 if radius != 0 :
111 j = 1
112 while j < sides:
113 t = angle * j
114 x = cos(t) * radius
115 y = sin(t) * radius
116 newpoints.append([x, y, 0])
117 j += 1
119 return newpoints
122 # ------------------------------------------------------------
123 # Ellipse:
125 def SimpleEllipse(a=2.0, b=1.0):
126 newpoints = []
128 newpoints.append([a, 0.0, 0.0])
129 newpoints.append([0.0, b, 0.0])
130 newpoints.append([-a, 0.0, 0.0])
131 newpoints.append([0.0, -b, 0.0])
133 return newpoints
136 # ------------------------------------------------------------
137 # Arc:
139 def SimpleArc(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
140 newpoints = []
142 startangle = radians(startangle)
143 endangle = radians(endangle)
144 sides += 1
146 angle = (endangle - startangle) / sides
147 x = cos(startangle) * radius
148 y = sin(startangle) * radius
149 newpoints.append([x, y, 0])
150 j = 1
151 while j < sides:
152 t = angle * j
153 x = cos(t + startangle) * radius
154 y = sin(t + startangle) * radius
155 newpoints.append([x, y, 0])
156 j += 1
157 x = cos(endangle) * radius
158 y = sin(endangle) * radius
159 newpoints.append([x, y, 0])
161 return newpoints
164 # ------------------------------------------------------------
165 # Sector:
167 def SimpleSector(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
168 newpoints = []
170 startangle = radians(startangle)
171 endangle = radians(endangle)
172 sides += 1
174 newpoints.append([0, 0, 0])
175 angle = (endangle - startangle) / sides
176 x = cos(startangle) * radius
177 y = sin(startangle) * radius
178 newpoints.append([x, y, 0])
179 j = 1
180 while j < sides:
181 t = angle * j
182 x = cos(t + startangle) * radius
183 y = sin(t + startangle) * radius
184 newpoints.append([x, y, 0])
185 j += 1
186 x = cos(endangle) * radius
187 y = sin(endangle) * radius
188 newpoints.append([x, y, 0])
190 return newpoints
193 # ------------------------------------------------------------
194 # Segment:
196 def SimpleSegment(sides=0, a=2.0, b=1.0, startangle=0.0, endangle=45.0):
197 newpoints = []
199 startangle = radians(startangle)
200 endangle = radians(endangle)
201 sides += 1
203 angle = (endangle - startangle) / sides
204 x = cos(startangle) * a
205 y = sin(startangle) * a
206 newpoints.append([x, y, 0])
207 j = 1
208 while j < sides:
209 t = angle * j
210 x = cos(t + startangle) * a
211 y = sin(t + startangle) * a
212 newpoints.append([x, y, 0])
213 j += 1
214 x = cos(endangle) * a
215 y = sin(endangle) * a
216 newpoints.append([x, y, 0])
218 x = cos(endangle) * b
219 y = sin(endangle) * b
220 newpoints.append([x, y, 0])
221 j = sides - 1
222 while j > 0:
223 t = angle * j
224 x = cos(t + startangle) * b
225 y = sin(t + startangle) * b
226 newpoints.append([x, y, 0])
227 j -= 1
228 x = cos(startangle) * b
229 y = sin(startangle) * b
230 newpoints.append([x, y, 0])
232 return newpoints
235 # ------------------------------------------------------------
236 # Rectangle:
238 def SimpleRectangle(width=2.0, length=2.0, rounded=0.0, center=True):
239 newpoints = []
241 r = rounded / 2
243 if center:
244 x = width / 2
245 y = length / 2
246 if rounded != 0.0:
247 newpoints.append([-x + r, y, 0.0])
248 newpoints.append([x - r, y, 0.0])
249 newpoints.append([x, y - r, 0.0])
250 newpoints.append([x, -y + r, 0.0])
251 newpoints.append([x - r, -y, 0.0])
252 newpoints.append([-x + r, -y, 0.0])
253 newpoints.append([-x, -y + r, 0.0])
254 newpoints.append([-x, y - r, 0.0])
255 else:
256 newpoints.append([-x, y, 0.0])
257 newpoints.append([x, y, 0.0])
258 newpoints.append([x, -y, 0.0])
259 newpoints.append([-x, -y, 0.0])
261 else:
262 x = width
263 y = length
264 if rounded != 0.0:
265 newpoints.append([r, y, 0.0])
266 newpoints.append([x - r, y, 0.0])
267 newpoints.append([x, y - r, 0.0])
268 newpoints.append([x, r, 0.0])
269 newpoints.append([x - r, 0.0, 0.0])
270 newpoints.append([r, 0.0, 0.0])
271 newpoints.append([0.0, r, 0.0])
272 newpoints.append([0.0, y - r, 0.0])
273 else:
274 newpoints.append([0.0, 0.0, 0.0])
275 newpoints.append([0.0, y, 0.0])
276 newpoints.append([x, y, 0.0])
277 newpoints.append([x, 0.0, 0.0])
279 return newpoints
282 # ------------------------------------------------------------
283 # Rhomb:
285 def SimpleRhomb(width=2.0, length=2.0, center=True):
286 newpoints = []
287 x = width / 2
288 y = length / 2
290 if center:
291 newpoints.append([-x, 0.0, 0.0])
292 newpoints.append([0.0, y, 0.0])
293 newpoints.append([x, 0.0, 0.0])
294 newpoints.append([0.0, -y, 0.0])
295 else:
296 newpoints.append([x, 0.0, 0.0])
297 newpoints.append([0.0, y, 0.0])
298 newpoints.append([x, length, 0.0])
299 newpoints.append([width, y, 0.0])
301 return newpoints
304 # ------------------------------------------------------------
305 # Polygon:
307 def SimplePolygon(sides=3, radius=1.0):
308 newpoints = []
309 angle = radians(360.0) / sides
310 j = 0
312 while j < sides:
313 t = angle * j
314 x = sin(t) * radius
315 y = cos(t) * radius
316 newpoints.append([x, y, 0.0])
317 j += 1
319 return newpoints
322 # ------------------------------------------------------------
323 # Polygon_ab:
325 def SimplePolygon_ab(sides=3, a=2.0, b=1.0):
326 newpoints = []
327 angle = radians(360.0) / sides
328 j = 0
330 while j < sides:
331 t = angle * j
332 x = sin(t) * a
333 y = cos(t) * b
334 newpoints.append([x, y, 0.0])
335 j += 1
337 return newpoints
340 # ------------------------------------------------------------
341 # Trapezoid:
343 def SimpleTrapezoid(a=2.0, b=1.0, h=1.0, center=True):
344 newpoints = []
345 x = a / 2
346 y = b / 2
347 r = h / 2
349 if center:
350 newpoints.append([-x, -r, 0.0])
351 newpoints.append([-y, r, 0.0])
352 newpoints.append([y, r, 0.0])
353 newpoints.append([x, -r, 0.0])
355 else:
356 newpoints.append([0.0, 0.0, 0.0])
357 newpoints.append([x - y, h, 0.0])
358 newpoints.append([x + y, h, 0.0])
359 newpoints.append([a, 0.0, 0.0])
361 return newpoints
364 # ------------------------------------------------------------
365 # get array of vertcoordinates according to splinetype
366 def vertsToPoints(Verts, splineType):
368 # main vars
369 vertArray = []
371 # array for BEZIER spline output (V3)
372 if splineType == 'BEZIER':
373 for v in Verts:
374 vertArray += v
376 # array for nonBEZIER output (V4)
377 else:
378 for v in Verts:
379 vertArray += v
380 if splineType == 'NURBS':
381 # for nurbs w=1
382 vertArray.append(1)
383 else:
384 # for poly w=0
385 vertArray.append(0)
386 return vertArray
389 # ------------------------------------------------------------
390 # Main Function
392 def main(context, self, use_enter_edit_mode):
393 # output splineType 'POLY' 'NURBS' 'BEZIER'
394 splineType = self.outputType
396 sides = abs(int((self.Simple_endangle - self.Simple_startangle) / 90))
398 # get verts
399 if self.Simple_Type == 'Point':
400 verts = SimplePoint()
402 if self.Simple_Type == 'Line':
403 verts = SimpleLine(self.location, self.Simple_endlocation)
405 if self.Simple_Type == 'Distance':
406 verts = SimpleDistance(self.Simple_length, self.Simple_center)
408 if self.Simple_Type == 'Angle':
409 verts = SimpleAngle(self.Simple_length, self.Simple_angle)
411 if self.Simple_Type == 'Circle':
412 if self.Simple_sides < 4:
413 self.Simple_sides = 4
414 if self.Simple_radius == 0:
415 return {'FINISHED'}
416 verts = SimpleCircle(self.Simple_sides, self.Simple_radius)
418 if self.Simple_Type == 'Ellipse':
419 verts = SimpleEllipse(self.Simple_a, self.Simple_b)
421 if self.Simple_Type == 'Arc':
422 if self.Simple_sides < sides:
423 self.Simple_sides = sides
424 if self.Simple_radius == 0:
425 return {'FINISHED'}
426 verts = SimpleArc(
427 self.Simple_sides, self.Simple_radius,
428 self.Simple_startangle, self.Simple_endangle
431 if self.Simple_Type == 'Sector':
432 if self.Simple_sides < sides:
433 self.Simple_sides = sides
434 if self.Simple_radius == 0:
435 return {'FINISHED'}
436 verts = SimpleSector(
437 self.Simple_sides, self.Simple_radius,
438 self.Simple_startangle, self.Simple_endangle
441 if self.Simple_Type == 'Segment':
442 if self.Simple_sides < sides:
443 self.Simple_sides = sides
444 if self.Simple_a == 0 or self.Simple_b == 0 or self.Simple_a == self.Simple_b:
445 return {'FINISHED'}
446 if self.Simple_a > self.Simple_b:
447 verts = SimpleSegment(
448 self.Simple_sides, self.Simple_a, self.Simple_b,
449 self.Simple_startangle, self.Simple_endangle
451 if self.Simple_a < self.Simple_b:
452 verts = SimpleSegment(
453 self.Simple_sides, self.Simple_b, self.Simple_a,
454 self.Simple_startangle, self.Simple_endangle
457 if self.Simple_Type == 'Rectangle':
458 verts = SimpleRectangle(
459 self.Simple_width, self.Simple_length,
460 self.Simple_rounded, self.Simple_center
463 if self.Simple_Type == 'Rhomb':
464 verts = SimpleRhomb(
465 self.Simple_width, self.Simple_length, self.Simple_center
468 if self.Simple_Type == 'Polygon':
469 if self.Simple_sides < 3:
470 self.Simple_sides = 3
471 verts = SimplePolygon(
472 self.Simple_sides, self.Simple_radius
475 if self.Simple_Type == 'Polygon_ab':
476 if self.Simple_sides < 3:
477 self.Simple_sides = 3
478 verts = SimplePolygon_ab(
479 self.Simple_sides, self.Simple_a, self.Simple_b
482 if self.Simple_Type == 'Trapezoid':
483 verts = SimpleTrapezoid(
484 self.Simple_a, self.Simple_b, self.Simple_h, self.Simple_center
487 # turn verts into array
488 vertArray = vertsToPoints(verts, splineType)
490 # create object
491 if bpy.context.mode == 'EDIT_CURVE':
493 Curve = context.active_object
494 newSpline = Curve.data.splines.new(type=splineType) # spline
495 else:
496 name = self.Simple_Type # Type as name
498 dataCurve = bpy.data.curves.new(name, type='CURVE') # curve data block
499 newSpline = dataCurve.splines.new(type=splineType) # spline
501 # create object with new Curve
502 Curve = object_utils.object_data_add(context, dataCurve, operator=self) # place in active scene
503 Curve.select_set(True)
505 for spline in Curve.data.splines:
506 if spline.type == 'BEZIER':
507 for point in spline.bezier_points:
508 point.select_control_point = False
509 point.select_left_handle = False
510 point.select_right_handle = False
511 else:
512 for point in spline.points:
513 point.select = False
515 # create spline from vertarray
516 all_points = []
517 if splineType == 'BEZIER':
518 newSpline.bezier_points.add(int(len(vertArray) * 0.33))
519 newSpline.bezier_points.foreach_set('co', vertArray)
520 for point in newSpline.bezier_points:
521 point.handle_right_type = self.handleType
522 point.handle_left_type = self.handleType
523 point.select_control_point = True
524 point.select_left_handle = True
525 point.select_right_handle = True
526 all_points.append(point)
527 else:
528 newSpline.points.add(int(len(vertArray) * 0.25 - 1))
529 newSpline.points.foreach_set('co', vertArray)
530 newSpline.use_endpoint_u = True
531 for point in newSpline.points:
532 all_points.append(point)
533 point.select = True
535 n = len(all_points)
537 d = 2 * 0.27606262
539 if splineType == 'BEZIER':
540 if self.Simple_Type == 'Circle' or self.Simple_Type == 'Arc' or \
541 self.Simple_Type == 'Sector' or self.Simple_Type == 'Segment' or \
542 self.Simple_Type == 'Ellipse':
544 for p in all_points:
545 p.handle_right_type = 'FREE'
546 p.handle_left_type = 'FREE'
548 if self.Simple_Type == 'Circle':
549 i = 0
550 for p1 in all_points:
551 if i != (n - 1):
552 p2 = all_points[i + 1]
553 u1 = asin(p1.co.y / self.Simple_radius)
554 u2 = asin(p2.co.y / self.Simple_radius)
555 if p1.co.x > 0 and p2.co.x < 0:
556 u1 = acos(p1.co.x / self.Simple_radius)
557 u2 = acos(p2.co.x / self.Simple_radius)
558 elif p1.co.x < 0 and p2.co.x > 0:
559 u1 = acos(p1.co.x / self.Simple_radius)
560 u2 = acos(p2.co.x / self.Simple_radius)
561 u = u2 - u1
562 if u < 0:
563 u = -u
564 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
565 v1 = Vector((-p1.co.y, p1.co.x, 0))
566 v1.normalize()
567 v2 = Vector((-p2.co.y, p2.co.x, 0))
568 v2.normalize()
569 vh1 = v1 * l
570 vh2 = v2 * l
571 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
572 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
573 p1.handle_right = v1
574 p2.handle_left = v2
575 if i == (n - 1):
576 p2 = all_points[0]
577 u1 = asin(p1.co.y / self.Simple_radius)
578 u2 = asin(p2.co.y / self.Simple_radius)
579 if p1.co.x > 0 and p2.co.x < 0:
580 u1 = acos(p1.co.x / self.Simple_radius)
581 u2 = acos(p2.co.x / self.Simple_radius)
582 elif p1.co.x < 0 and p2.co.x > 0:
583 u1 = acos(p1.co.x / self.Simple_radius)
584 u2 = acos(p2.co.x / self.Simple_radius)
585 u = u2 - u1
586 if u < 0:
587 u = -u
588 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
589 v1 = Vector((-p1.co.y, p1.co.x, 0))
590 v1.normalize()
591 v2 = Vector((-p2.co.y, p2.co.x, 0))
592 v2.normalize()
593 vh1 = v1 * l
594 vh2 = v2 * l
595 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
596 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
597 p1.handle_right = v1
598 p2.handle_left = v2
599 i += 1
601 if self.Simple_Type == 'Ellipse':
602 all_points[0].handle_right = Vector((self.Simple_a, self.Simple_b * d, 0))
603 all_points[0].handle_left = Vector((self.Simple_a, -self.Simple_b * d, 0))
604 all_points[1].handle_right = Vector((-self.Simple_a * d, self.Simple_b, 0))
605 all_points[1].handle_left = Vector((self.Simple_a * d, self.Simple_b, 0))
606 all_points[2].handle_right = Vector((-self.Simple_a, -self.Simple_b * d, 0))
607 all_points[2].handle_left = Vector((-self.Simple_a, self.Simple_b * d, 0))
608 all_points[3].handle_right = Vector((self.Simple_a * d, -self.Simple_b, 0))
609 all_points[3].handle_left = Vector((-self.Simple_a * d, -self.Simple_b, 0))
611 if self.Simple_Type == 'Arc':
612 i = 0
613 for p1 in all_points:
614 if i != (n - 1):
615 p2 = all_points[i + 1]
616 u1 = asin(p1.co.y / self.Simple_radius)
617 u2 = asin(p2.co.y / self.Simple_radius)
618 if p1.co.x > 0 and p2.co.x < 0:
619 u1 = acos(p1.co.x / self.Simple_radius)
620 u2 = acos(p2.co.x / self.Simple_radius)
621 elif p1.co.x < 0 and p2.co.x > 0:
622 u1 = acos(p1.co.x / self.Simple_radius)
623 u2 = acos(p2.co.x / self.Simple_radius)
624 u = u2 - u1
625 if u < 0:
626 u = -u
627 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
628 v1 = Vector((-p1.co.y, p1.co.x, 0))
629 v1.normalize()
630 v2 = Vector((-p2.co.y, p2.co.x, 0))
631 v2.normalize()
632 vh1 = v1 * l
633 vh2 = v2 * l
634 if self.Simple_startangle < self.Simple_endangle:
635 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
636 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
637 p1.handle_right = v1
638 p2.handle_left = v2
639 else:
640 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
641 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
642 p1.handle_right = v1
643 p2.handle_left = v2
644 i += 1
645 all_points[0].handle_left_type = 'VECTOR'
646 all_points[-1].handle_right_type = 'VECTOR'
648 if self.Simple_Type == 'Sector':
649 i = 0
650 for p1 in all_points:
651 if i == 0:
652 p1.handle_right_type = 'VECTOR'
653 p1.handle_left_type = 'VECTOR'
654 elif i != (n - 1):
655 p2 = all_points[i + 1]
656 u1 = asin(p1.co.y / self.Simple_radius)
657 u2 = asin(p2.co.y / self.Simple_radius)
658 if p1.co.x > 0 and p2.co.x < 0:
659 u1 = acos(p1.co.x / self.Simple_radius)
660 u2 = acos(p2.co.x / self.Simple_radius)
661 elif p1.co.x < 0 and p2.co.x > 0:
662 u1 = acos(p1.co.x / self.Simple_radius)
663 u2 = acos(p2.co.x / self.Simple_radius)
664 u = u2 - u1
665 if u < 0:
666 u = -u
667 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
668 v1 = Vector((-p1.co.y, p1.co.x, 0))
669 v1.normalize()
670 v2 = Vector((-p2.co.y, p2.co.x, 0))
671 v2.normalize()
672 vh1 = v1 * l
673 vh2 = v2 * l
674 if self.Simple_startangle < self.Simple_endangle:
675 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
676 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
677 p1.handle_right = v1
678 p2.handle_left = v2
679 else:
680 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
681 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
682 p1.handle_right = v1
683 p2.handle_left = v2
684 i += 1
685 all_points[0].handle_left_type = 'VECTOR'
686 all_points[0].handle_right_type = 'VECTOR'
687 all_points[1].handle_left_type = 'VECTOR'
688 all_points[-1].handle_right_type = 'VECTOR'
690 if self.Simple_Type == 'Segment':
691 i = 0
692 if self.Simple_a > self.Simple_b:
693 Segment_a = self.Simple_a
694 Segment_b = self.Simple_b
695 if self.Simple_a < self.Simple_b:
696 Segment_b = self.Simple_a
697 Segment_a = self.Simple_b
698 for p1 in all_points:
699 if i < (n / 2 - 1):
700 p2 = all_points[i + 1]
701 u1 = asin(p1.co.y / Segment_a)
702 u2 = asin(p2.co.y / Segment_a)
703 if p1.co.x > 0 and p2.co.x < 0:
704 u1 = acos(p1.co.x / Segment_a)
705 u2 = acos(p2.co.x / Segment_a)
706 elif p1.co.x < 0 and p2.co.x > 0:
707 u1 = acos(p1.co.x / Segment_a)
708 u2 = acos(p2.co.x / Segment_a)
709 u = u2 - u1
710 if u < 0:
711 u = -u
712 l = 4 / 3 * tan(1 / 4 * u) * Segment_a
713 v1 = Vector((-p1.co.y, p1.co.x, 0))
714 v1.normalize()
715 v2 = Vector((-p2.co.y, p2.co.x, 0))
716 v2.normalize()
717 vh1 = v1 * l
718 vh2 = v2 * l
719 if self.Simple_startangle < self.Simple_endangle:
720 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
721 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
722 p1.handle_right = v1
723 p2.handle_left = v2
724 else:
725 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
726 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
727 p1.handle_right = v1
728 p2.handle_left = v2
729 elif i != (n / 2 - 1) and i != (n - 1):
730 p2 = all_points[i + 1]
731 u1 = asin(p1.co.y / Segment_b)
732 u2 = asin(p2.co.y / Segment_b)
733 if p1.co.x > 0 and p2.co.x < 0:
734 u1 = acos(p1.co.x / Segment_b)
735 u2 = acos(p2.co.x / Segment_b)
736 elif p1.co.x < 0 and p2.co.x > 0:
737 u1 = acos(p1.co.x / Segment_b)
738 u2 = acos(p2.co.x / Segment_b)
739 u = u2 - u1
740 if u < 0:
741 u = -u
742 l = 4 / 3 * tan(1 / 4 * u) * Segment_b
743 v1 = Vector((-p1.co.y, p1.co.x, 0))
744 v1.normalize()
745 v2 = Vector((-p2.co.y, p2.co.x, 0))
746 v2.normalize()
747 vh1 = v1 * l
748 vh2 = v2 * l
749 if self.Simple_startangle < self.Simple_endangle:
750 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
751 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
752 p1.handle_right = v1
753 p2.handle_left = v2
754 else:
755 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
756 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
757 p1.handle_right = v1
758 p2.handle_left = v2
760 i += 1
761 all_points[0].handle_left_type = 'VECTOR'
762 all_points[n - 1].handle_right_type = 'VECTOR'
763 all_points[int(n / 2) - 1].handle_right_type = 'VECTOR'
764 all_points[int(n / 2)].handle_left_type = 'VECTOR'
766 # set newSpline Options
767 newSpline.use_cyclic_u = self.use_cyclic_u
768 newSpline.use_endpoint_u = self.endp_u
769 newSpline.order_u = self.order_u
771 # set curve Options
772 Curve.data.dimensions = self.shape
773 Curve.data.use_path = True
774 if self.shape == '3D':
775 Curve.data.fill_mode = 'FULL'
776 else:
777 Curve.data.fill_mode = 'BOTH'
779 # move and rotate spline in edit mode
780 if bpy.context.mode == 'EDIT_CURVE':
781 if self.align == "WORLD":
782 location = self.location - context.active_object.location
783 bpy.ops.transform.translate(value = location, orient_type='GLOBAL')
784 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X', orient_type='GLOBAL')
785 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y', orient_type='GLOBAL')
786 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z', orient_type='GLOBAL')
788 elif self.align == "VIEW":
789 bpy.ops.transform.translate(value = self.location)
790 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
791 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
792 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
794 elif self.align == "CURSOR":
795 location = context.active_object.location
796 self.location = bpy.context.scene.cursor.location - location
797 self.rotation = bpy.context.scene.cursor.rotation_euler
799 bpy.ops.transform.translate(value = self.location)
800 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
801 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
802 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
805 def menu(self, context):
806 oper1 = self.layout.operator(Simple.bl_idname, text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
807 oper1.Simple_Type = "Angle"
808 oper1.use_cyclic_u = False
810 oper2 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_THICKNESS")
811 oper2.Simple_Type = "Arc"
812 oper2.use_cyclic_u = False
814 oper3 = self.layout.operator(Simple.bl_idname, text="Circle", icon="ANTIALIASED")
815 oper3.Simple_Type = "Circle"
816 oper3.use_cyclic_u = True
818 oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="DRIVER_DISTANCE")
819 oper4.Simple_Type = "Distance"
820 oper4.use_cyclic_u = False
822 oper5 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MESH_TORUS")
823 oper5.Simple_Type = "Ellipse"
824 oper5.use_cyclic_u = True
826 oper6 = self.layout.operator(Simple.bl_idname, text="Line", icon="MOD_SIMPLIFY")
827 oper6.Simple_Type = "Line"
828 oper6.use_cyclic_u = False
829 oper6.shape = '3D'
831 oper7 = self.layout.operator(Simple.bl_idname, text="Point", icon="LAYER_ACTIVE")
832 oper7.Simple_Type = "Point"
833 oper7.use_cyclic_u = False
835 oper8 = self.layout.operator(Simple.bl_idname, text="Polygon", icon="SEQ_CHROMA_SCOPE")
836 oper8.Simple_Type = "Polygon"
837 oper8.use_cyclic_u = True
839 oper9 = self.layout.operator(Simple.bl_idname, text="Polygon ab", icon="SEQ_CHROMA_SCOPE")
840 oper9.Simple_Type = "Polygon_ab"
841 oper9.use_cyclic_u = True
843 oper10 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MESH_PLANE")
844 oper10.Simple_Type = "Rectangle"
845 oper10.use_cyclic_u = True
847 oper11 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="DECORATE_ANIMATE")
848 oper11.Simple_Type = "Rhomb"
849 oper11.use_cyclic_u = True
851 oper12 = self.layout.operator(Simple.bl_idname, text="Sector", icon="CON_SHRINKWRAP")
852 oper12.Simple_Type = "Sector"
853 oper12.use_cyclic_u = True
855 oper13 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_SIMPLEDEFORM")
856 oper13.Simple_Type = "Segment"
857 oper13.use_cyclic_u = True
859 oper14 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_EDGESPLIT")
860 oper14.Simple_Type = "Trapezoid"
861 oper14.use_cyclic_u = True
863 # ------------------------------------------------------------
864 # Simple operator
866 class Simple(Operator, object_utils.AddObjectHelper):
867 bl_idname = "curve.simple"
868 bl_label = "Simple Curve"
869 bl_description = "Construct a Simple Curve"
870 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
872 # change properties
873 Simple : BoolProperty(
874 name="Simple",
875 default=True,
876 description="Simple Curve"
878 Simple_Change : BoolProperty(
879 name="Change",
880 default=False,
881 description="Change Simple Curve"
883 Simple_Delete : StringProperty(
884 name="Delete",
885 description="Delete Simple Curve"
887 # general properties
888 Types = [('Point', "Point", "Construct a Point"),
889 ('Line', "Line", "Construct a Line"),
890 ('Distance', "Distance", "Construct a two point Distance"),
891 ('Angle', "Angle", "Construct an Angle"),
892 ('Circle', "Circle", "Construct a Circle"),
893 ('Ellipse', "Ellipse", "Construct an Ellipse"),
894 ('Arc', "Arc", "Construct an Arc"),
895 ('Sector', "Sector", "Construct a Sector"),
896 ('Segment', "Segment", "Construct a Segment"),
897 ('Rectangle', "Rectangle", "Construct a Rectangle"),
898 ('Rhomb', "Rhomb", "Construct a Rhomb"),
899 ('Polygon', "Polygon", "Construct a Polygon"),
900 ('Polygon_ab', "Polygon ab", "Construct a Polygon ab"),
901 ('Trapezoid', "Trapezoid", "Construct a Trapezoid")
903 Simple_Type : EnumProperty(
904 name="Type",
905 description="Form of Curve to create",
906 items=Types
908 # Line properties
909 Simple_endlocation : FloatVectorProperty(
910 name="",
911 description="End location",
912 default=(2.0, 2.0, 2.0),
913 subtype='TRANSLATION'
915 # Trapezoid properties
916 Simple_a : FloatProperty(
917 name="Side a",
918 default=2.0,
919 min=0.0, soft_min=0.0,
920 unit='LENGTH',
921 description="a side Value"
923 Simple_b : FloatProperty(
924 name="Side b",
925 default=1.0,
926 min=0.0, soft_min=0.0,
927 unit='LENGTH',
928 description="b side Value"
930 Simple_h : FloatProperty(
931 name="Height",
932 default=1.0,
933 unit='LENGTH',
934 description="Height of the Trapezoid - distance between a and b"
936 Simple_angle : FloatProperty(
937 name="Angle",
938 default=45.0,
939 description="Angle"
941 Simple_startangle : FloatProperty(
942 name="Start angle",
943 default=0.0,
944 min=-360.0, soft_min=-360.0,
945 max=360.0, soft_max=360.0,
946 description="Start angle"
948 Simple_endangle : FloatProperty(
949 name="End angle",
950 default=45.0,
951 min=-360.0, soft_min=-360.0,
952 max=360.0, soft_max=360.0,
953 description="End angle"
955 Simple_sides : IntProperty(
956 name="Sides",
957 default=3,
958 min=0, soft_min=0,
959 description="Sides"
961 Simple_radius : FloatProperty(
962 name="Radius",
963 default=1.0,
964 min=0.0, soft_min=0.0,
965 unit='LENGTH',
966 description="Radius"
968 Simple_center : BoolProperty(
969 name="Length center",
970 default=True,
971 description="Length center"
974 Angle_types = [('Degrees', "Degrees", "Use Degrees"),
975 ('Radians', "Radians", "Use Radians")]
976 Simple_degrees_or_radians : EnumProperty(
977 name="Degrees or radians",
978 description="Degrees or radians",
979 items=Angle_types
981 # Rectangle properties
982 Simple_width : FloatProperty(
983 name="Width",
984 default=2.0,
985 min=0.0, soft_min=0,
986 unit='LENGTH',
987 description="Width"
989 Simple_length : FloatProperty(
990 name="Length",
991 default=2.0,
992 min=0.0, soft_min=0.0,
993 unit='LENGTH',
994 description="Length"
996 Simple_rounded : FloatProperty(
997 name="Rounded",
998 default=0.0,
999 min=0.0, soft_min=0.0,
1000 unit='LENGTH',
1001 description="Rounded corners"
1003 # Curve Options
1004 shapeItems = [
1005 ('2D', "2D", "2D shape Curve"),
1006 ('3D', "3D", "3D shape Curve")]
1007 shape : EnumProperty(
1008 name="2D / 3D",
1009 items=shapeItems,
1010 description="2D or 3D Curve"
1012 outputType : EnumProperty(
1013 name="Output splines",
1014 description="Type of splines to output",
1015 items=[
1016 ('POLY', "Poly", "Poly Spline type"),
1017 ('NURBS', "Nurbs", "Nurbs Spline type"),
1018 ('BEZIER', "Bezier", "Bezier Spline type")],
1019 default='BEZIER'
1021 use_cyclic_u : BoolProperty(
1022 name="Cyclic",
1023 default=True,
1024 description="make curve closed"
1026 endp_u : BoolProperty(
1027 name="Use endpoint u",
1028 default=True,
1029 description="stretch to endpoints"
1031 order_u : IntProperty(
1032 name="Order u",
1033 default=4,
1034 min=2, soft_min=2,
1035 max=6, soft_max=6,
1036 description="Order of nurbs spline"
1038 handleType : EnumProperty(
1039 name="Handle type",
1040 default='VECTOR',
1041 description="Bezier handles type",
1042 items=[
1043 ('VECTOR', "Vector", "Vector type Bezier handles"),
1044 ('AUTO', "Auto", "Automatic type Bezier handles")]
1046 edit_mode : BoolProperty(
1047 name="Show in edit mode",
1048 default=True,
1049 description="Show in edit mode"
1052 def draw(self, context):
1053 layout = self.layout
1055 # general options
1056 col = layout.column()
1057 col.prop(self, "Simple_Type")
1059 l = 0
1060 s = 0
1062 if self.Simple_Type == 'Line':
1063 box = layout.box()
1064 col = box.column(align=True)
1065 col.label(text=self.Simple_Type + " Options:")
1066 col.prop(self, "Simple_endlocation")
1067 v = Vector(self.Simple_endlocation) - Vector(self.location)
1068 l = v.length
1070 if self.Simple_Type == 'Distance':
1071 box = layout.box()
1072 col = box.column(align=True)
1073 col.label(text=self.Simple_Type + " Options:")
1074 col.prop(self, "Simple_length")
1075 col.prop(self, "Simple_center")
1076 l = self.Simple_length
1078 if self.Simple_Type == 'Angle':
1079 box = layout.box()
1080 col = box.column(align=True)
1081 col.label(text=self.Simple_Type + " Options:")
1082 col.prop(self, "Simple_length")
1083 col.prop(self, "Simple_angle")
1085 if self.Simple_Type == 'Circle':
1086 box = layout.box()
1087 col = box.column(align=True)
1088 col.label(text=self.Simple_Type + " Options:")
1089 col.prop(self, "Simple_sides")
1090 col.prop(self, "Simple_radius")
1092 l = 2 * pi * abs(self.Simple_radius)
1093 s = pi * self.Simple_radius * self.Simple_radius
1095 if self.Simple_Type == 'Ellipse':
1096 box = layout.box()
1097 col = box.column(align=True)
1098 col.label(text=self.Simple_Type + " Options:")
1099 col.prop(self, "Simple_a", text="Radius a")
1100 col.prop(self, "Simple_b", text="Radius b")
1102 l = pi * (3 * (self.Simple_a + self.Simple_b) -
1103 sqrt((3 * self.Simple_a + self.Simple_b) *
1104 (self.Simple_a + 3 * self.Simple_b)))
1106 s = pi * abs(self.Simple_b) * abs(self.Simple_a)
1108 if self.Simple_Type == 'Arc':
1109 box = layout.box()
1110 col = box.column(align=True)
1111 col.label(text=self.Simple_Type + " Options:")
1112 col.prop(self, "Simple_sides")
1113 col.prop(self, "Simple_radius")
1115 col = box.column(align=True)
1116 col.prop(self, "Simple_startangle")
1117 col.prop(self, "Simple_endangle")
1118 #row = layout.row()
1119 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1121 l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180)
1123 if self.Simple_Type == 'Sector':
1124 box = layout.box()
1125 col = box.column(align=True)
1126 col.label(text=self.Simple_Type + " Options:")
1127 col.prop(self, "Simple_sides")
1128 col.prop(self, "Simple_radius")
1130 col = box.column(align=True)
1131 col.prop(self, "Simple_startangle")
1132 col.prop(self, "Simple_endangle")
1133 #row = layout.row()
1134 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1136 l = abs(pi * self.Simple_radius *
1137 (self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2
1139 s = pi * self.Simple_radius * self.Simple_radius * \
1140 abs(self.Simple_endangle - self.Simple_startangle) / 360
1142 if self.Simple_Type == 'Segment':
1143 box = layout.box()
1144 col = box.column(align=True)
1145 col.label(text=self.Simple_Type + " Options:")
1146 col.prop(self, "Simple_sides")
1147 col.prop(self, "Simple_a", text="Radius a")
1148 col.prop(self, "Simple_b", text="Radius b")
1150 col = box.column(align=True)
1151 col.prop(self, "Simple_startangle")
1152 col.prop(self, "Simple_endangle")
1154 #row = layout.row()
1155 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1157 la = abs(pi * self.Simple_a * (self.Simple_endangle - self.Simple_startangle) / 180)
1158 lb = abs(pi * self.Simple_b * (self.Simple_endangle - self.Simple_startangle) / 180)
1159 l = abs(self.Simple_a - self.Simple_b) * 2 + la + lb
1161 sa = pi * self.Simple_a * self.Simple_a * \
1162 abs(self.Simple_endangle - self.Simple_startangle) / 360
1164 sb = pi * self.Simple_b * self.Simple_b * \
1165 abs(self.Simple_endangle - self.Simple_startangle) / 360
1167 s = abs(sa - sb)
1169 if self.Simple_Type == 'Rectangle':
1170 box = layout.box()
1171 col = box.column(align=True)
1172 col.label(text=self.Simple_Type + " Options:")
1173 col.prop(self, "Simple_width")
1174 col.prop(self, "Simple_length")
1175 col.prop(self, "Simple_rounded")
1177 box.prop(self, "Simple_center")
1178 l = 2 * abs(self.Simple_width) + 2 * abs(self.Simple_length)
1179 s = abs(self.Simple_width) * abs(self.Simple_length)
1181 if self.Simple_Type == 'Rhomb':
1182 box = layout.box()
1183 col = box.column(align=True)
1184 col.label(text=self.Simple_Type + " Options:")
1185 col.prop(self, "Simple_width")
1186 col.prop(self, "Simple_length")
1187 col.prop(self, "Simple_center")
1189 g = hypot(self.Simple_width / 2, self.Simple_length / 2)
1190 l = 4 * g
1191 s = self.Simple_width * self.Simple_length / 2
1193 if self.Simple_Type == 'Polygon':
1194 box = layout.box()
1195 col = box.column(align=True)
1196 col.label(text=self.Simple_Type + " Options:")
1197 col.prop(self, "Simple_sides")
1198 col.prop(self, "Simple_radius")
1200 if self.Simple_Type == 'Polygon_ab':
1201 box = layout.box()
1202 col = box.column(align=True)
1203 col.label(text="Polygon ab Options:")
1204 col.prop(self, "Simple_sides")
1205 col.prop(self, "Simple_a")
1206 col.prop(self, "Simple_b")
1208 if self.Simple_Type == 'Trapezoid':
1209 box = layout.box()
1210 col = box.column(align=True)
1211 col.label(text=self.Simple_Type + " Options:")
1212 col.prop(self, "Simple_a")
1213 col.prop(self, "Simple_b")
1214 col.prop(self, "Simple_h")
1216 box.prop(self, "Simple_center")
1217 g = hypot(self.Simple_h, (self.Simple_a - self.Simple_b) / 2)
1218 l = self.Simple_a + self.Simple_b + g * 2
1219 s = (abs(self.Simple_a) + abs(self.Simple_b)) / 2 * self.Simple_h
1221 row = layout.row()
1222 row.prop(self, "shape", expand=True)
1224 # output options
1225 col = layout.column()
1226 col.label(text="Output Curve Type:")
1227 col.row().prop(self, "outputType", expand=True)
1229 if self.outputType == 'NURBS':
1230 col.prop(self, "order_u")
1231 elif self.outputType == 'BEZIER':
1232 col.row().prop(self, 'handleType', expand=True)
1234 col = layout.column()
1235 col.row().prop(self, "use_cyclic_u", expand=True)
1237 col = layout.column()
1238 col.row().prop(self, "edit_mode", expand=True)
1240 col = layout.column()
1241 # AddObjectHelper props
1242 col.prop(self, "align")
1243 col.prop(self, "location")
1244 col.prop(self, "rotation")
1246 if l != 0 or s != 0:
1247 box = layout.box()
1248 box.label(text="Statistics:", icon="INFO")
1249 if l != 0:
1250 l_str = str(round(l, 4))
1251 box.label(text="Length: " + l_str)
1252 if s != 0:
1253 s_str = str(round(s, 4))
1254 box.label(text="Area: " + s_str)
1256 @classmethod
1257 def poll(cls, context):
1258 return context.scene is not None
1260 def execute(self, context):
1262 # turn off 'Enter Edit Mode'
1263 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
1264 bpy.context.preferences.edit.use_enter_edit_mode = False
1266 # main function
1267 main(context, self, use_enter_edit_mode)
1269 if use_enter_edit_mode:
1270 bpy.ops.object.mode_set(mode = 'EDIT')
1272 # restore pre operator state
1273 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
1275 if self.edit_mode:
1276 bpy.ops.object.mode_set(mode = 'EDIT')
1277 else:
1278 bpy.ops.object.mode_set(mode = 'OBJECT')
1280 return {'FINISHED'}
1282 def invoke(self, context, event):
1284 self.execute(context)
1286 return {'FINISHED'}
1288 # Register
1289 classes = [
1290 Simple,
1293 def register():
1294 from bpy.utils import register_class
1295 for cls in classes:
1296 register_class(cls)
1298 bpy.types.VIEW3D_MT_curve_add.append(menu)
1300 def unregister():
1301 from bpy.utils import unregister_class
1302 for cls in reversed(classes):
1303 unregister_class(cls)
1305 bpy.types.VIEW3D_MT_curve_add.remove(menu)
1307 if __name__ == "__main__":
1308 register()