Merge branch 'blender-v4.0-release'
[blender-addons.git] / add_curve_extra_objects / add_curve_simple.py
blob0f994641eb136f3626fa23597d3402af2d5b947f
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 "name": "Simple Curve",
7 "author": "Vladimir Spivak (cwolf3d)",
8 "version": (1, 6, 1),
9 "blender": (2, 80, 0),
10 "location": "View3D > Add > Curve",
11 "description": "Adds Simple Curve",
12 "warning": "",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
14 "category": "Add Curve",
18 # ------------------------------------------------------------
20 import bpy
21 from bpy_extras import object_utils
22 from bpy.types import (
23 Operator,
24 Menu,
25 Panel,
26 PropertyGroup,
28 from bpy.props import (
29 BoolProperty,
30 EnumProperty,
31 FloatProperty,
32 FloatVectorProperty,
33 IntProperty,
34 StringProperty,
35 PointerProperty,
37 from mathutils import (
38 Vector,
39 Matrix,
41 from math import (
42 sin, asin, sqrt,
43 acos, cos, pi,
44 radians, tan,
45 hypot,
47 # from bpy_extras.object_utils import *
50 # ------------------------------------------------------------
51 # Point:
53 def SimplePoint():
54 newpoints = []
56 newpoints.append([0.0, 0.0, 0.0])
58 return newpoints
61 # ------------------------------------------------------------
62 # Line:
64 def SimpleLine(c1=[0.0, 0.0, 0.0], c2=[2.0, 2.0, 2.0]):
65 newpoints = []
67 c3 = Vector(c2) - Vector(c1)
68 newpoints.append([0.0, 0.0, 0.0])
69 newpoints.append([c3[0], c3[1], c3[2]])
71 return newpoints
74 # ------------------------------------------------------------
75 # Angle:
77 def SimpleAngle(length=1.0, angle=45.0):
78 newpoints = []
80 angle = radians(angle)
81 newpoints.append([length, 0.0, 0.0])
82 newpoints.append([0.0, 0.0, 0.0])
83 newpoints.append([length * cos(angle), length * sin(angle), 0.0])
85 return newpoints
88 # ------------------------------------------------------------
89 # Distance:
91 def SimpleDistance(length=1.0, center=True):
92 newpoints = []
94 if center:
95 newpoints.append([-length / 2, 0.0, 0.0])
96 newpoints.append([length / 2, 0.0, 0.0])
97 else:
98 newpoints.append([0.0, 0.0, 0.0])
99 newpoints.append([length, 0.0, 0.0])
101 return newpoints
104 # ------------------------------------------------------------
105 # Circle:
107 def SimpleCircle(sides=4, radius=1.0):
108 newpoints = []
110 angle = radians(360) / sides
111 newpoints.append([radius, 0, 0])
112 if radius != 0 :
113 j = 1
114 while j < sides:
115 t = angle * j
116 x = cos(t) * radius
117 y = sin(t) * radius
118 newpoints.append([x, y, 0])
119 j += 1
121 return newpoints
124 # ------------------------------------------------------------
125 # Ellipse:
127 def SimpleEllipse(a=2.0, b=1.0):
128 newpoints = []
130 newpoints.append([a, 0.0, 0.0])
131 newpoints.append([0.0, b, 0.0])
132 newpoints.append([-a, 0.0, 0.0])
133 newpoints.append([0.0, -b, 0.0])
135 return newpoints
138 # ------------------------------------------------------------
139 # Arc:
141 def SimpleArc(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
142 newpoints = []
144 startangle = radians(startangle)
145 endangle = radians(endangle)
146 sides += 1
148 angle = (endangle - startangle) / sides
149 x = cos(startangle) * radius
150 y = sin(startangle) * radius
151 newpoints.append([x, y, 0])
152 j = 1
153 while j < sides:
154 t = angle * j
155 x = cos(t + startangle) * radius
156 y = sin(t + startangle) * radius
157 newpoints.append([x, y, 0])
158 j += 1
159 x = cos(endangle) * radius
160 y = sin(endangle) * radius
161 newpoints.append([x, y, 0])
163 return newpoints
166 # ------------------------------------------------------------
167 # Sector:
169 def SimpleSector(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
170 newpoints = []
172 startangle = radians(startangle)
173 endangle = radians(endangle)
174 sides += 1
176 newpoints.append([0, 0, 0])
177 angle = (endangle - startangle) / sides
178 x = cos(startangle) * radius
179 y = sin(startangle) * radius
180 newpoints.append([x, y, 0])
181 j = 1
182 while j < sides:
183 t = angle * j
184 x = cos(t + startangle) * radius
185 y = sin(t + startangle) * radius
186 newpoints.append([x, y, 0])
187 j += 1
188 x = cos(endangle) * radius
189 y = sin(endangle) * radius
190 newpoints.append([x, y, 0])
192 return newpoints
195 # ------------------------------------------------------------
196 # Segment:
198 def SimpleSegment(sides=0, a=2.0, b=1.0, startangle=0.0, endangle=45.0):
199 newpoints = []
201 startangle = radians(startangle)
202 endangle = radians(endangle)
203 sides += 1
205 angle = (endangle - startangle) / sides
206 x = cos(startangle) * a
207 y = sin(startangle) * a
208 newpoints.append([x, y, 0])
209 j = 1
210 while j < sides:
211 t = angle * j
212 x = cos(t + startangle) * a
213 y = sin(t + startangle) * a
214 newpoints.append([x, y, 0])
215 j += 1
216 x = cos(endangle) * a
217 y = sin(endangle) * a
218 newpoints.append([x, y, 0])
220 x = cos(endangle) * b
221 y = sin(endangle) * b
222 newpoints.append([x, y, 0])
223 j = sides - 1
224 while j > 0:
225 t = angle * j
226 x = cos(t + startangle) * b
227 y = sin(t + startangle) * b
228 newpoints.append([x, y, 0])
229 j -= 1
230 x = cos(startangle) * b
231 y = sin(startangle) * b
232 newpoints.append([x, y, 0])
234 return newpoints
237 # ------------------------------------------------------------
238 # Rectangle:
240 def SimpleRectangle(width=2.0, length=2.0, rounded=0.0, center=True):
241 newpoints = []
243 r = rounded / 2
245 if center:
246 x = width / 2
247 y = length / 2
248 if rounded != 0.0:
249 newpoints.append([-x + r, y, 0.0])
250 newpoints.append([x - r, y, 0.0])
251 newpoints.append([x, y - r, 0.0])
252 newpoints.append([x, -y + r, 0.0])
253 newpoints.append([x - r, -y, 0.0])
254 newpoints.append([-x + r, -y, 0.0])
255 newpoints.append([-x, -y + r, 0.0])
256 newpoints.append([-x, y - r, 0.0])
257 else:
258 newpoints.append([-x, y, 0.0])
259 newpoints.append([x, y, 0.0])
260 newpoints.append([x, -y, 0.0])
261 newpoints.append([-x, -y, 0.0])
263 else:
264 x = width
265 y = length
266 if rounded != 0.0:
267 newpoints.append([r, y, 0.0])
268 newpoints.append([x - r, y, 0.0])
269 newpoints.append([x, y - r, 0.0])
270 newpoints.append([x, r, 0.0])
271 newpoints.append([x - r, 0.0, 0.0])
272 newpoints.append([r, 0.0, 0.0])
273 newpoints.append([0.0, r, 0.0])
274 newpoints.append([0.0, y - r, 0.0])
275 else:
276 newpoints.append([0.0, 0.0, 0.0])
277 newpoints.append([0.0, y, 0.0])
278 newpoints.append([x, y, 0.0])
279 newpoints.append([x, 0.0, 0.0])
281 return newpoints
284 # ------------------------------------------------------------
285 # Rhomb:
287 def SimpleRhomb(width=2.0, length=2.0, center=True):
288 newpoints = []
289 x = width / 2
290 y = length / 2
292 if center:
293 newpoints.append([-x, 0.0, 0.0])
294 newpoints.append([0.0, y, 0.0])
295 newpoints.append([x, 0.0, 0.0])
296 newpoints.append([0.0, -y, 0.0])
297 else:
298 newpoints.append([x, 0.0, 0.0])
299 newpoints.append([0.0, y, 0.0])
300 newpoints.append([x, length, 0.0])
301 newpoints.append([width, y, 0.0])
303 return newpoints
306 # ------------------------------------------------------------
307 # Polygon:
309 def SimplePolygon(sides=3, radius=1.0):
310 newpoints = []
311 angle = radians(360.0) / sides
312 j = 0
314 while j < sides:
315 t = angle * j
316 x = sin(t) * radius
317 y = cos(t) * radius
318 newpoints.append([x, y, 0.0])
319 j += 1
321 return newpoints
324 # ------------------------------------------------------------
325 # Polygon_ab:
327 def SimplePolygon_ab(sides=3, a=2.0, b=1.0):
328 newpoints = []
329 angle = radians(360.0) / sides
330 j = 0
332 while j < sides:
333 t = angle * j
334 x = sin(t) * a
335 y = cos(t) * b
336 newpoints.append([x, y, 0.0])
337 j += 1
339 return newpoints
342 # ------------------------------------------------------------
343 # Trapezoid:
345 def SimpleTrapezoid(a=2.0, b=1.0, h=1.0, center=True):
346 newpoints = []
347 x = a / 2
348 y = b / 2
349 r = h / 2
351 if center:
352 newpoints.append([-x, -r, 0.0])
353 newpoints.append([-y, r, 0.0])
354 newpoints.append([y, r, 0.0])
355 newpoints.append([x, -r, 0.0])
357 else:
358 newpoints.append([0.0, 0.0, 0.0])
359 newpoints.append([x - y, h, 0.0])
360 newpoints.append([x + y, h, 0.0])
361 newpoints.append([a, 0.0, 0.0])
363 return newpoints
366 # ------------------------------------------------------------
367 # get array of vertcoordinates according to splinetype
368 def vertsToPoints(Verts, splineType):
370 # main vars
371 vertArray = []
373 # array for BEZIER spline output (V3)
374 if splineType == 'BEZIER':
375 for v in Verts:
376 vertArray += v
378 # array for nonBEZIER output (V4)
379 else:
380 for v in Verts:
381 vertArray += v
382 if splineType == 'NURBS':
383 # for nurbs w=1
384 vertArray.append(1)
385 else:
386 # for poly w=0
387 vertArray.append(0)
388 return vertArray
391 # ------------------------------------------------------------
392 # Main Function
394 def main(context, self, use_enter_edit_mode):
395 # output splineType 'POLY' 'NURBS' 'BEZIER'
396 splineType = self.outputType
398 sides = abs(int((self.Simple_endangle - self.Simple_startangle) / 90))
400 # get verts
401 if self.Simple_Type == 'Point':
402 verts = SimplePoint()
404 if self.Simple_Type == 'Line':
405 verts = SimpleLine(self.location, self.Simple_endlocation)
407 if self.Simple_Type == 'Distance':
408 verts = SimpleDistance(self.Simple_length, self.Simple_center)
410 if self.Simple_Type == 'Angle':
411 verts = SimpleAngle(self.Simple_length, self.Simple_angle)
413 if self.Simple_Type == 'Circle':
414 if self.Simple_sides < 4:
415 self.Simple_sides = 4
416 if self.Simple_radius == 0:
417 return {'FINISHED'}
418 verts = SimpleCircle(self.Simple_sides, self.Simple_radius)
420 if self.Simple_Type == 'Ellipse':
421 verts = SimpleEllipse(self.Simple_a, self.Simple_b)
423 if self.Simple_Type == 'Arc':
424 if self.Simple_sides < sides:
425 self.Simple_sides = sides
426 if self.Simple_radius == 0:
427 return {'FINISHED'}
428 verts = SimpleArc(
429 self.Simple_sides, self.Simple_radius,
430 self.Simple_startangle, self.Simple_endangle
433 if self.Simple_Type == 'Sector':
434 if self.Simple_sides < sides:
435 self.Simple_sides = sides
436 if self.Simple_radius == 0:
437 return {'FINISHED'}
438 verts = SimpleSector(
439 self.Simple_sides, self.Simple_radius,
440 self.Simple_startangle, self.Simple_endangle
443 if self.Simple_Type == 'Segment':
444 if self.Simple_sides < sides:
445 self.Simple_sides = sides
446 if self.Simple_a == 0 or self.Simple_b == 0 or self.Simple_a == self.Simple_b:
447 return {'FINISHED'}
448 if self.Simple_a > self.Simple_b:
449 verts = SimpleSegment(
450 self.Simple_sides, self.Simple_a, self.Simple_b,
451 self.Simple_startangle, self.Simple_endangle
453 if self.Simple_a < self.Simple_b:
454 verts = SimpleSegment(
455 self.Simple_sides, self.Simple_b, self.Simple_a,
456 self.Simple_startangle, self.Simple_endangle
459 if self.Simple_Type == 'Rectangle':
460 verts = SimpleRectangle(
461 self.Simple_width, self.Simple_length,
462 self.Simple_rounded, self.Simple_center
465 if self.Simple_Type == 'Rhomb':
466 verts = SimpleRhomb(
467 self.Simple_width, self.Simple_length, self.Simple_center
470 if self.Simple_Type == 'Polygon':
471 if self.Simple_sides < 3:
472 self.Simple_sides = 3
473 verts = SimplePolygon(
474 self.Simple_sides, self.Simple_radius
477 if self.Simple_Type == 'Polygon_ab':
478 if self.Simple_sides < 3:
479 self.Simple_sides = 3
480 verts = SimplePolygon_ab(
481 self.Simple_sides, self.Simple_a, self.Simple_b
484 if self.Simple_Type == 'Trapezoid':
485 verts = SimpleTrapezoid(
486 self.Simple_a, self.Simple_b, self.Simple_h, self.Simple_center
489 # turn verts into array
490 vertArray = vertsToPoints(verts, splineType)
492 # create object
493 if bpy.context.mode == 'EDIT_CURVE':
495 Curve = context.active_object
496 newSpline = Curve.data.splines.new(type=splineType) # spline
497 else:
498 name = self.Simple_Type # Type as name
500 dataCurve = bpy.data.curves.new(name, type='CURVE') # curve data block
501 newSpline = dataCurve.splines.new(type=splineType) # spline
503 # create object with new Curve
504 Curve = object_utils.object_data_add(context, dataCurve, operator=self) # place in active scene
505 Curve.select_set(True)
507 for spline in Curve.data.splines:
508 if spline.type == 'BEZIER':
509 for point in spline.bezier_points:
510 point.select_control_point = False
511 point.select_left_handle = False
512 point.select_right_handle = False
513 else:
514 for point in spline.points:
515 point.select = False
517 # create spline from vertarray
518 all_points = []
519 if splineType == 'BEZIER':
520 newSpline.bezier_points.add(int(len(vertArray) * 0.33))
521 newSpline.bezier_points.foreach_set('co', vertArray)
522 for point in newSpline.bezier_points:
523 point.handle_right_type = self.handleType
524 point.handle_left_type = self.handleType
525 point.select_control_point = True
526 point.select_left_handle = True
527 point.select_right_handle = True
528 all_points.append(point)
529 else:
530 newSpline.points.add(int(len(vertArray) * 0.25 - 1))
531 newSpline.points.foreach_set('co', vertArray)
532 newSpline.use_endpoint_u = True
533 for point in newSpline.points:
534 all_points.append(point)
535 point.select = True
537 n = len(all_points)
539 d = 2 * 0.27606262
541 if splineType == 'BEZIER':
542 if self.Simple_Type == 'Circle' or self.Simple_Type == 'Arc' or \
543 self.Simple_Type == 'Sector' or self.Simple_Type == 'Segment' or \
544 self.Simple_Type == 'Ellipse':
546 for p in all_points:
547 p.handle_right_type = 'FREE'
548 p.handle_left_type = 'FREE'
550 if self.Simple_Type == 'Circle':
551 i = 0
552 for p1 in all_points:
553 if i != (n - 1):
554 p2 = all_points[i + 1]
555 u1 = asin(p1.co.y / self.Simple_radius)
556 u2 = asin(p2.co.y / self.Simple_radius)
557 if p1.co.x > 0 and p2.co.x < 0:
558 u1 = acos(p1.co.x / self.Simple_radius)
559 u2 = acos(p2.co.x / self.Simple_radius)
560 elif p1.co.x < 0 and p2.co.x > 0:
561 u1 = acos(p1.co.x / self.Simple_radius)
562 u2 = acos(p2.co.x / self.Simple_radius)
563 u = u2 - u1
564 if u < 0:
565 u = -u
566 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
567 v1 = Vector((-p1.co.y, p1.co.x, 0))
568 v1.normalize()
569 v2 = Vector((-p2.co.y, p2.co.x, 0))
570 v2.normalize()
571 vh1 = v1 * l
572 vh2 = v2 * l
573 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
574 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
575 p1.handle_right = v1
576 p2.handle_left = v2
577 if i == (n - 1):
578 p2 = all_points[0]
579 u1 = asin(p1.co.y / self.Simple_radius)
580 u2 = asin(p2.co.y / self.Simple_radius)
581 if p1.co.x > 0 and p2.co.x < 0:
582 u1 = acos(p1.co.x / self.Simple_radius)
583 u2 = acos(p2.co.x / self.Simple_radius)
584 elif p1.co.x < 0 and p2.co.x > 0:
585 u1 = acos(p1.co.x / self.Simple_radius)
586 u2 = acos(p2.co.x / self.Simple_radius)
587 u = u2 - u1
588 if u < 0:
589 u = -u
590 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
591 v1 = Vector((-p1.co.y, p1.co.x, 0))
592 v1.normalize()
593 v2 = Vector((-p2.co.y, p2.co.x, 0))
594 v2.normalize()
595 vh1 = v1 * l
596 vh2 = v2 * l
597 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
598 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
599 p1.handle_right = v1
600 p2.handle_left = v2
601 i += 1
603 if self.Simple_Type == 'Ellipse':
604 all_points[0].handle_right = Vector((self.Simple_a, self.Simple_b * d, 0))
605 all_points[0].handle_left = Vector((self.Simple_a, -self.Simple_b * d, 0))
606 all_points[1].handle_right = Vector((-self.Simple_a * d, self.Simple_b, 0))
607 all_points[1].handle_left = Vector((self.Simple_a * d, self.Simple_b, 0))
608 all_points[2].handle_right = Vector((-self.Simple_a, -self.Simple_b * d, 0))
609 all_points[2].handle_left = Vector((-self.Simple_a, self.Simple_b * d, 0))
610 all_points[3].handle_right = Vector((self.Simple_a * d, -self.Simple_b, 0))
611 all_points[3].handle_left = Vector((-self.Simple_a * d, -self.Simple_b, 0))
613 if self.Simple_Type == 'Arc':
614 i = 0
615 for p1 in all_points:
616 if i != (n - 1):
617 p2 = all_points[i + 1]
618 u1 = asin(p1.co.y / self.Simple_radius)
619 u2 = asin(p2.co.y / self.Simple_radius)
620 if p1.co.x > 0 and p2.co.x < 0:
621 u1 = acos(p1.co.x / self.Simple_radius)
622 u2 = acos(p2.co.x / self.Simple_radius)
623 elif p1.co.x < 0 and p2.co.x > 0:
624 u1 = acos(p1.co.x / self.Simple_radius)
625 u2 = acos(p2.co.x / self.Simple_radius)
626 u = u2 - u1
627 if u < 0:
628 u = -u
629 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
630 v1 = Vector((-p1.co.y, p1.co.x, 0))
631 v1.normalize()
632 v2 = Vector((-p2.co.y, p2.co.x, 0))
633 v2.normalize()
634 vh1 = v1 * l
635 vh2 = v2 * l
636 if self.Simple_startangle < self.Simple_endangle:
637 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
638 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
639 p1.handle_right = v1
640 p2.handle_left = v2
641 else:
642 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
643 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
644 p1.handle_right = v1
645 p2.handle_left = v2
646 i += 1
647 all_points[0].handle_left_type = 'VECTOR'
648 all_points[-1].handle_right_type = 'VECTOR'
650 if self.Simple_Type == 'Sector':
651 i = 0
652 for p1 in all_points:
653 if i == 0:
654 p1.handle_right_type = 'VECTOR'
655 p1.handle_left_type = 'VECTOR'
656 elif i != (n - 1):
657 p2 = all_points[i + 1]
658 u1 = asin(p1.co.y / self.Simple_radius)
659 u2 = asin(p2.co.y / self.Simple_radius)
660 if p1.co.x > 0 and p2.co.x < 0:
661 u1 = acos(p1.co.x / self.Simple_radius)
662 u2 = acos(p2.co.x / self.Simple_radius)
663 elif p1.co.x < 0 and p2.co.x > 0:
664 u1 = acos(p1.co.x / self.Simple_radius)
665 u2 = acos(p2.co.x / self.Simple_radius)
666 u = u2 - u1
667 if u < 0:
668 u = -u
669 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
670 v1 = Vector((-p1.co.y, p1.co.x, 0))
671 v1.normalize()
672 v2 = Vector((-p2.co.y, p2.co.x, 0))
673 v2.normalize()
674 vh1 = v1 * l
675 vh2 = v2 * l
676 if self.Simple_startangle < self.Simple_endangle:
677 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
678 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
679 p1.handle_right = v1
680 p2.handle_left = v2
681 else:
682 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
683 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
684 p1.handle_right = v1
685 p2.handle_left = v2
686 i += 1
687 all_points[0].handle_left_type = 'VECTOR'
688 all_points[0].handle_right_type = 'VECTOR'
689 all_points[1].handle_left_type = 'VECTOR'
690 all_points[-1].handle_right_type = 'VECTOR'
692 if self.Simple_Type == 'Segment':
693 i = 0
694 if self.Simple_a > self.Simple_b:
695 Segment_a = self.Simple_a
696 Segment_b = self.Simple_b
697 if self.Simple_a < self.Simple_b:
698 Segment_b = self.Simple_a
699 Segment_a = self.Simple_b
700 for p1 in all_points:
701 if i < (n / 2 - 1):
702 p2 = all_points[i + 1]
703 u1 = asin(p1.co.y / Segment_a)
704 u2 = asin(p2.co.y / Segment_a)
705 if p1.co.x > 0 and p2.co.x < 0:
706 u1 = acos(p1.co.x / Segment_a)
707 u2 = acos(p2.co.x / Segment_a)
708 elif p1.co.x < 0 and p2.co.x > 0:
709 u1 = acos(p1.co.x / Segment_a)
710 u2 = acos(p2.co.x / Segment_a)
711 u = u2 - u1
712 if u < 0:
713 u = -u
714 l = 4 / 3 * tan(1 / 4 * u) * Segment_a
715 v1 = Vector((-p1.co.y, p1.co.x, 0))
716 v1.normalize()
717 v2 = Vector((-p2.co.y, p2.co.x, 0))
718 v2.normalize()
719 vh1 = v1 * l
720 vh2 = v2 * l
721 if self.Simple_startangle < self.Simple_endangle:
722 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
723 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
724 p1.handle_right = v1
725 p2.handle_left = v2
726 else:
727 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
728 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
729 p1.handle_right = v1
730 p2.handle_left = v2
731 elif i != (n / 2 - 1) and i != (n - 1):
732 p2 = all_points[i + 1]
733 u1 = asin(p1.co.y / Segment_b)
734 u2 = asin(p2.co.y / Segment_b)
735 if p1.co.x > 0 and p2.co.x < 0:
736 u1 = acos(p1.co.x / Segment_b)
737 u2 = acos(p2.co.x / Segment_b)
738 elif p1.co.x < 0 and p2.co.x > 0:
739 u1 = acos(p1.co.x / Segment_b)
740 u2 = acos(p2.co.x / Segment_b)
741 u = u2 - u1
742 if u < 0:
743 u = -u
744 l = 4 / 3 * tan(1 / 4 * u) * Segment_b
745 v1 = Vector((-p1.co.y, p1.co.x, 0))
746 v1.normalize()
747 v2 = Vector((-p2.co.y, p2.co.x, 0))
748 v2.normalize()
749 vh1 = v1 * l
750 vh2 = v2 * l
751 if self.Simple_startangle < self.Simple_endangle:
752 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
753 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
754 p1.handle_right = v1
755 p2.handle_left = v2
756 else:
757 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
758 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
759 p1.handle_right = v1
760 p2.handle_left = v2
762 i += 1
763 all_points[0].handle_left_type = 'VECTOR'
764 all_points[n - 1].handle_right_type = 'VECTOR'
765 all_points[int(n / 2) - 1].handle_right_type = 'VECTOR'
766 all_points[int(n / 2)].handle_left_type = 'VECTOR'
768 # set newSpline Options
769 newSpline.use_cyclic_u = self.use_cyclic_u
770 newSpline.use_endpoint_u = self.endp_u
771 newSpline.order_u = self.order_u
773 # set curve Options
774 Curve.data.dimensions = self.shape
775 Curve.data.use_path = True
776 if self.shape == '3D':
777 Curve.data.fill_mode = 'FULL'
778 else:
779 Curve.data.fill_mode = 'BOTH'
781 # move and rotate spline in edit mode
782 if bpy.context.mode == 'EDIT_CURVE':
783 if self.align == "WORLD":
784 location = self.location - context.active_object.location
785 bpy.ops.transform.translate(value = location, orient_type='GLOBAL')
786 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X', orient_type='GLOBAL')
787 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y', orient_type='GLOBAL')
788 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z', orient_type='GLOBAL')
790 elif self.align == "VIEW":
791 bpy.ops.transform.translate(value = self.location)
792 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
793 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
794 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
796 elif self.align == "CURSOR":
797 location = context.active_object.location
798 self.location = bpy.context.scene.cursor.location - location
799 self.rotation = bpy.context.scene.cursor.rotation_euler
801 bpy.ops.transform.translate(value = self.location)
802 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
803 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
804 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
807 def menu(self, context):
808 oper1 = self.layout.operator(Simple.bl_idname, text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
809 oper1.Simple_Type = "Angle"
810 oper1.use_cyclic_u = False
812 oper2 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_THICKNESS")
813 oper2.Simple_Type = "Arc"
814 oper2.use_cyclic_u = False
816 oper3 = self.layout.operator(Simple.bl_idname, text="Circle", icon="ANTIALIASED")
817 oper3.Simple_Type = "Circle"
818 oper3.use_cyclic_u = True
820 oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="DRIVER_DISTANCE")
821 oper4.Simple_Type = "Distance"
822 oper4.use_cyclic_u = False
824 oper5 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MESH_TORUS")
825 oper5.Simple_Type = "Ellipse"
826 oper5.use_cyclic_u = True
828 oper6 = self.layout.operator(Simple.bl_idname, text="Line", icon="MOD_SIMPLIFY")
829 oper6.Simple_Type = "Line"
830 oper6.use_cyclic_u = False
831 oper6.shape = '3D'
833 oper7 = self.layout.operator(Simple.bl_idname, text="Point", icon="LAYER_ACTIVE")
834 oper7.Simple_Type = "Point"
835 oper7.use_cyclic_u = False
837 oper8 = self.layout.operator(Simple.bl_idname, text="Polygon", icon="SEQ_CHROMA_SCOPE")
838 oper8.Simple_Type = "Polygon"
839 oper8.use_cyclic_u = True
841 oper9 = self.layout.operator(Simple.bl_idname, text="Polygon ab", icon="SEQ_CHROMA_SCOPE")
842 oper9.Simple_Type = "Polygon_ab"
843 oper9.use_cyclic_u = True
845 oper10 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MESH_PLANE")
846 oper10.Simple_Type = "Rectangle"
847 oper10.use_cyclic_u = True
849 oper11 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="DECORATE_ANIMATE")
850 oper11.Simple_Type = "Rhomb"
851 oper11.use_cyclic_u = True
853 oper12 = self.layout.operator(Simple.bl_idname, text="Sector", icon="CON_SHRINKWRAP")
854 oper12.Simple_Type = "Sector"
855 oper12.use_cyclic_u = True
857 oper13 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_SIMPLEDEFORM")
858 oper13.Simple_Type = "Segment"
859 oper13.use_cyclic_u = True
861 oper14 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_EDGESPLIT")
862 oper14.Simple_Type = "Trapezoid"
863 oper14.use_cyclic_u = True
865 # ------------------------------------------------------------
866 # Simple operator
868 class Simple(Operator, object_utils.AddObjectHelper):
869 bl_idname = "curve.simple"
870 bl_label = "Simple Curve"
871 bl_description = "Construct a Simple Curve"
872 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
874 # change properties
875 Simple : BoolProperty(
876 name="Simple",
877 default=True,
878 description="Simple Curve"
880 Simple_Change : BoolProperty(
881 name="Change",
882 default=False,
883 description="Change Simple Curve"
885 Simple_Delete : StringProperty(
886 name="Delete",
887 description="Delete Simple Curve"
889 # general properties
890 Types = [('Point', "Point", "Construct a Point"),
891 ('Line', "Line", "Construct a Line"),
892 ('Distance', "Distance", "Construct a two point Distance"),
893 ('Angle', "Angle", "Construct an Angle"),
894 ('Circle', "Circle", "Construct a Circle"),
895 ('Ellipse', "Ellipse", "Construct an Ellipse"),
896 ('Arc', "Arc", "Construct an Arc"),
897 ('Sector', "Sector", "Construct a Sector"),
898 ('Segment', "Segment", "Construct a Segment"),
899 ('Rectangle', "Rectangle", "Construct a Rectangle"),
900 ('Rhomb', "Rhomb", "Construct a Rhomb"),
901 ('Polygon', "Polygon", "Construct a Polygon"),
902 ('Polygon_ab', "Polygon ab", "Construct a Polygon ab"),
903 ('Trapezoid', "Trapezoid", "Construct a Trapezoid")
905 Simple_Type : EnumProperty(
906 name="Type",
907 description="Form of Curve to create",
908 items=Types
910 # Line properties
911 Simple_endlocation : FloatVectorProperty(
912 name="",
913 description="End location",
914 default=(2.0, 2.0, 2.0),
915 subtype='TRANSLATION'
917 # Trapezoid properties
918 Simple_a : FloatProperty(
919 name="Side a",
920 default=2.0,
921 min=0.0, soft_min=0.0,
922 unit='LENGTH',
923 description="a side Value"
925 Simple_b : FloatProperty(
926 name="Side b",
927 default=1.0,
928 min=0.0, soft_min=0.0,
929 unit='LENGTH',
930 description="b side Value"
932 Simple_h : FloatProperty(
933 name="Height",
934 default=1.0,
935 unit='LENGTH',
936 description="Height of the Trapezoid - distance between a and b"
938 Simple_angle : FloatProperty(
939 name="Angle",
940 default=45.0,
941 description="Angle"
943 Simple_startangle : FloatProperty(
944 name="Start angle",
945 default=0.0,
946 min=-360.0, soft_min=-360.0,
947 max=360.0, soft_max=360.0,
948 description="Start angle"
950 Simple_endangle : FloatProperty(
951 name="End angle",
952 default=45.0,
953 min=-360.0, soft_min=-360.0,
954 max=360.0, soft_max=360.0,
955 description="End angle"
957 Simple_sides : IntProperty(
958 name="Sides",
959 default=3,
960 min=0, soft_min=0,
961 description="Sides"
963 Simple_radius : FloatProperty(
964 name="Radius",
965 default=1.0,
966 min=0.0, soft_min=0.0,
967 unit='LENGTH',
968 description="Radius"
970 Simple_center : BoolProperty(
971 name="Length center",
972 default=True,
973 description="Length center"
976 Angle_types = [('Degrees', "Degrees", "Use Degrees"),
977 ('Radians', "Radians", "Use Radians")]
978 Simple_degrees_or_radians : EnumProperty(
979 name="Degrees or radians",
980 description="Degrees or radians",
981 items=Angle_types
983 # Rectangle properties
984 Simple_width : FloatProperty(
985 name="Width",
986 default=2.0,
987 min=0.0, soft_min=0,
988 unit='LENGTH',
989 description="Width"
991 Simple_length : FloatProperty(
992 name="Length",
993 default=2.0,
994 min=0.0, soft_min=0.0,
995 unit='LENGTH',
996 description="Length"
998 Simple_rounded : FloatProperty(
999 name="Rounded",
1000 default=0.0,
1001 min=0.0, soft_min=0.0,
1002 unit='LENGTH',
1003 description="Rounded corners"
1005 # Curve Options
1006 shapeItems = [
1007 ('2D', "2D", "2D shape Curve"),
1008 ('3D', "3D", "3D shape Curve")]
1009 shape : EnumProperty(
1010 name="2D / 3D",
1011 items=shapeItems,
1012 description="2D or 3D Curve"
1014 outputType : EnumProperty(
1015 name="Output splines",
1016 description="Type of splines to output",
1017 items=[
1018 ('POLY', "Poly", "Poly Spline type"),
1019 ('NURBS', "Nurbs", "Nurbs Spline type"),
1020 ('BEZIER', "Bezier", "Bezier Spline type")],
1021 default='BEZIER'
1023 use_cyclic_u : BoolProperty(
1024 name="Cyclic",
1025 default=True,
1026 description="make curve closed"
1028 endp_u : BoolProperty(
1029 name="Use endpoint u",
1030 default=True,
1031 description="stretch to endpoints"
1033 order_u : IntProperty(
1034 name="Order u",
1035 default=4,
1036 min=2, soft_min=2,
1037 max=6, soft_max=6,
1038 description="Order of nurbs spline"
1040 handleType : EnumProperty(
1041 name="Handle type",
1042 default='VECTOR',
1043 description="Bezier handles type",
1044 items=[
1045 ('VECTOR', "Vector", "Vector type Bezier handles"),
1046 ('AUTO', "Auto", "Automatic type Bezier handles")]
1048 edit_mode : BoolProperty(
1049 name="Show in edit mode",
1050 default=True,
1051 description="Show in edit mode"
1054 def draw(self, context):
1055 layout = self.layout
1057 # general options
1058 col = layout.column()
1059 col.prop(self, "Simple_Type")
1061 l = 0
1062 s = 0
1064 if self.Simple_Type == 'Line':
1065 box = layout.box()
1066 col = box.column(align=True)
1067 col.label(text=self.Simple_Type + " Options:")
1068 col.prop(self, "Simple_endlocation")
1069 v = Vector(self.Simple_endlocation) - Vector(self.location)
1070 l = v.length
1072 if self.Simple_Type == 'Distance':
1073 box = layout.box()
1074 col = box.column(align=True)
1075 col.label(text=self.Simple_Type + " Options:")
1076 col.prop(self, "Simple_length")
1077 col.prop(self, "Simple_center")
1078 l = self.Simple_length
1080 if self.Simple_Type == 'Angle':
1081 box = layout.box()
1082 col = box.column(align=True)
1083 col.label(text=self.Simple_Type + " Options:")
1084 col.prop(self, "Simple_length")
1085 col.prop(self, "Simple_angle")
1087 if self.Simple_Type == 'Circle':
1088 box = layout.box()
1089 col = box.column(align=True)
1090 col.label(text=self.Simple_Type + " Options:")
1091 col.prop(self, "Simple_sides")
1092 col.prop(self, "Simple_radius")
1094 l = 2 * pi * abs(self.Simple_radius)
1095 s = pi * self.Simple_radius * self.Simple_radius
1097 if self.Simple_Type == 'Ellipse':
1098 box = layout.box()
1099 col = box.column(align=True)
1100 col.label(text=self.Simple_Type + " Options:")
1101 col.prop(self, "Simple_a", text="Radius a")
1102 col.prop(self, "Simple_b", text="Radius b")
1104 l = pi * (3 * (self.Simple_a + self.Simple_b) -
1105 sqrt((3 * self.Simple_a + self.Simple_b) *
1106 (self.Simple_a + 3 * self.Simple_b)))
1108 s = pi * abs(self.Simple_b) * abs(self.Simple_a)
1110 if self.Simple_Type == 'Arc':
1111 box = layout.box()
1112 col = box.column(align=True)
1113 col.label(text=self.Simple_Type + " Options:")
1114 col.prop(self, "Simple_sides")
1115 col.prop(self, "Simple_radius")
1117 col = box.column(align=True)
1118 col.prop(self, "Simple_startangle")
1119 col.prop(self, "Simple_endangle")
1120 #row = layout.row()
1121 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1123 l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180)
1125 if self.Simple_Type == 'Sector':
1126 box = layout.box()
1127 col = box.column(align=True)
1128 col.label(text=self.Simple_Type + " Options:")
1129 col.prop(self, "Simple_sides")
1130 col.prop(self, "Simple_radius")
1132 col = box.column(align=True)
1133 col.prop(self, "Simple_startangle")
1134 col.prop(self, "Simple_endangle")
1135 #row = layout.row()
1136 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1138 l = abs(pi * self.Simple_radius *
1139 (self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2
1141 s = pi * self.Simple_radius * self.Simple_radius * \
1142 abs(self.Simple_endangle - self.Simple_startangle) / 360
1144 if self.Simple_Type == 'Segment':
1145 box = layout.box()
1146 col = box.column(align=True)
1147 col.label(text=self.Simple_Type + " Options:")
1148 col.prop(self, "Simple_sides")
1149 col.prop(self, "Simple_a", text="Radius a")
1150 col.prop(self, "Simple_b", text="Radius b")
1152 col = box.column(align=True)
1153 col.prop(self, "Simple_startangle")
1154 col.prop(self, "Simple_endangle")
1156 #row = layout.row()
1157 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1159 la = abs(pi * self.Simple_a * (self.Simple_endangle - self.Simple_startangle) / 180)
1160 lb = abs(pi * self.Simple_b * (self.Simple_endangle - self.Simple_startangle) / 180)
1161 l = abs(self.Simple_a - self.Simple_b) * 2 + la + lb
1163 sa = pi * self.Simple_a * self.Simple_a * \
1164 abs(self.Simple_endangle - self.Simple_startangle) / 360
1166 sb = pi * self.Simple_b * self.Simple_b * \
1167 abs(self.Simple_endangle - self.Simple_startangle) / 360
1169 s = abs(sa - sb)
1171 if self.Simple_Type == 'Rectangle':
1172 box = layout.box()
1173 col = box.column(align=True)
1174 col.label(text=self.Simple_Type + " Options:")
1175 col.prop(self, "Simple_width")
1176 col.prop(self, "Simple_length")
1177 col.prop(self, "Simple_rounded")
1179 box.prop(self, "Simple_center")
1180 l = 2 * abs(self.Simple_width) + 2 * abs(self.Simple_length)
1181 s = abs(self.Simple_width) * abs(self.Simple_length)
1183 if self.Simple_Type == 'Rhomb':
1184 box = layout.box()
1185 col = box.column(align=True)
1186 col.label(text=self.Simple_Type + " Options:")
1187 col.prop(self, "Simple_width")
1188 col.prop(self, "Simple_length")
1189 col.prop(self, "Simple_center")
1191 g = hypot(self.Simple_width / 2, self.Simple_length / 2)
1192 l = 4 * g
1193 s = self.Simple_width * self.Simple_length / 2
1195 if self.Simple_Type == 'Polygon':
1196 box = layout.box()
1197 col = box.column(align=True)
1198 col.label(text=self.Simple_Type + " Options:")
1199 col.prop(self, "Simple_sides")
1200 col.prop(self, "Simple_radius")
1202 if self.Simple_Type == 'Polygon_ab':
1203 box = layout.box()
1204 col = box.column(align=True)
1205 col.label(text="Polygon ab Options:")
1206 col.prop(self, "Simple_sides")
1207 col.prop(self, "Simple_a")
1208 col.prop(self, "Simple_b")
1210 if self.Simple_Type == 'Trapezoid':
1211 box = layout.box()
1212 col = box.column(align=True)
1213 col.label(text=self.Simple_Type + " Options:")
1214 col.prop(self, "Simple_a")
1215 col.prop(self, "Simple_b")
1216 col.prop(self, "Simple_h")
1218 box.prop(self, "Simple_center")
1219 g = hypot(self.Simple_h, (self.Simple_a - self.Simple_b) / 2)
1220 l = self.Simple_a + self.Simple_b + g * 2
1221 s = (abs(self.Simple_a) + abs(self.Simple_b)) / 2 * self.Simple_h
1223 row = layout.row()
1224 row.prop(self, "shape", expand=True)
1226 # output options
1227 col = layout.column()
1228 col.label(text="Output Curve Type:")
1229 col.row().prop(self, "outputType", expand=True)
1231 if self.outputType == 'NURBS':
1232 col.prop(self, "order_u")
1233 elif self.outputType == 'BEZIER':
1234 col.row().prop(self, 'handleType', expand=True)
1236 col = layout.column()
1237 col.row().prop(self, "use_cyclic_u", expand=True)
1239 col = layout.column()
1240 col.row().prop(self, "edit_mode", expand=True)
1242 col = layout.column()
1243 # AddObjectHelper props
1244 col.prop(self, "align")
1245 col.prop(self, "location")
1246 col.prop(self, "rotation")
1248 if l != 0 or s != 0:
1249 box = layout.box()
1250 box.label(text="Statistics:", icon="INFO")
1251 if l != 0:
1252 l_str = str(round(l, 4))
1253 box.label(text="Length: " + l_str)
1254 if s != 0:
1255 s_str = str(round(s, 4))
1256 box.label(text="Area: " + s_str)
1258 @classmethod
1259 def poll(cls, context):
1260 return context.scene is not None
1262 def execute(self, context):
1264 # turn off 'Enter Edit Mode'
1265 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
1266 bpy.context.preferences.edit.use_enter_edit_mode = False
1268 # main function
1269 main(context, self, use_enter_edit_mode)
1271 if use_enter_edit_mode:
1272 bpy.ops.object.mode_set(mode = 'EDIT')
1274 # restore pre operator state
1275 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
1277 if self.edit_mode:
1278 bpy.ops.object.mode_set(mode = 'EDIT')
1279 else:
1280 bpy.ops.object.mode_set(mode = 'OBJECT')
1282 return {'FINISHED'}
1284 def invoke(self, context, event):
1286 self.execute(context)
1288 return {'FINISHED'}
1290 # Register
1291 classes = [
1292 Simple,
1295 def register():
1296 from bpy.utils import register_class
1297 for cls in classes:
1298 register_class(cls)
1300 bpy.types.VIEW3D_MT_curve_add.append(menu)
1302 def unregister():
1303 from bpy.utils import unregister_class
1304 for cls in reversed(classes):
1305 unregister_class(cls)
1307 bpy.types.VIEW3D_MT_curve_add.remove(menu)
1309 if __name__ == "__main__":
1310 register()