Merge branch 'blender-v2.92-release'
[blender-addons.git] / add_curve_extra_objects / add_curve_simple.py
blobc994acb96f44cfa40b76f77980fd1ab267c72877
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and / or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Simple Curve",
21 "author": "Vladimir Spivak (cwolf3d)",
22 "version": (1, 6, 1),
23 "blender": (2, 80, 0),
24 "location": "View3D > Add > Curve",
25 "description": "Adds Simple Curve",
26 "warning": "",
27 "doc_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
28 "Py/Scripts/Curve/Simple_curves",
29 "category": "Add Curve",
33 # ------------------------------------------------------------
35 import bpy
36 from bpy_extras import object_utils
37 from bpy.types import (
38 Operator,
39 Menu,
40 Panel,
41 PropertyGroup,
43 from bpy.props import (
44 BoolProperty,
45 EnumProperty,
46 FloatProperty,
47 FloatVectorProperty,
48 IntProperty,
49 StringProperty,
50 PointerProperty,
52 from mathutils import (
53 Vector,
54 Matrix,
56 from math import (
57 sin, asin, sqrt,
58 acos, cos, pi,
59 radians, tan,
60 hypot,
62 # from bpy_extras.object_utils import *
65 # ------------------------------------------------------------
66 # Point:
68 def SimplePoint():
69 newpoints = []
71 newpoints.append([0.0, 0.0, 0.0])
73 return newpoints
76 # ------------------------------------------------------------
77 # Line:
79 def SimpleLine(c1=[0.0, 0.0, 0.0], c2=[2.0, 2.0, 2.0]):
80 newpoints = []
82 c3 = Vector(c2) - Vector(c1)
83 newpoints.append([0.0, 0.0, 0.0])
84 newpoints.append([c3[0], c3[1], c3[2]])
86 return newpoints
89 # ------------------------------------------------------------
90 # Angle:
92 def SimpleAngle(length=1.0, angle=45.0):
93 newpoints = []
95 angle = radians(angle)
96 newpoints.append([length, 0.0, 0.0])
97 newpoints.append([0.0, 0.0, 0.0])
98 newpoints.append([length * cos(angle), length * sin(angle), 0.0])
100 return newpoints
103 # ------------------------------------------------------------
104 # Distance:
106 def SimpleDistance(length=1.0, center=True):
107 newpoints = []
109 if center:
110 newpoints.append([-length / 2, 0.0, 0.0])
111 newpoints.append([length / 2, 0.0, 0.0])
112 else:
113 newpoints.append([0.0, 0.0, 0.0])
114 newpoints.append([length, 0.0, 0.0])
116 return newpoints
119 # ------------------------------------------------------------
120 # Circle:
122 def SimpleCircle(sides=4, radius=1.0):
123 newpoints = []
125 angle = radians(360) / sides
126 newpoints.append([radius, 0, 0])
127 if radius != 0 :
128 j = 1
129 while j < sides:
130 t = angle * j
131 x = cos(t) * radius
132 y = sin(t) * radius
133 newpoints.append([x, y, 0])
134 j += 1
136 return newpoints
139 # ------------------------------------------------------------
140 # Ellipse:
142 def SimpleEllipse(a=2.0, b=1.0):
143 newpoints = []
145 newpoints.append([a, 0.0, 0.0])
146 newpoints.append([0.0, b, 0.0])
147 newpoints.append([-a, 0.0, 0.0])
148 newpoints.append([0.0, -b, 0.0])
150 return newpoints
153 # ------------------------------------------------------------
154 # Arc:
156 def SimpleArc(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
157 newpoints = []
159 startangle = radians(startangle)
160 endangle = radians(endangle)
161 sides += 1
163 angle = (endangle - startangle) / sides
164 x = cos(startangle) * radius
165 y = sin(startangle) * radius
166 newpoints.append([x, y, 0])
167 j = 1
168 while j < sides:
169 t = angle * j
170 x = cos(t + startangle) * radius
171 y = sin(t + startangle) * radius
172 newpoints.append([x, y, 0])
173 j += 1
174 x = cos(endangle) * radius
175 y = sin(endangle) * radius
176 newpoints.append([x, y, 0])
178 return newpoints
181 # ------------------------------------------------------------
182 # Sector:
184 def SimpleSector(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
185 newpoints = []
187 startangle = radians(startangle)
188 endangle = radians(endangle)
189 sides += 1
191 newpoints.append([0, 0, 0])
192 angle = (endangle - startangle) / sides
193 x = cos(startangle) * radius
194 y = sin(startangle) * radius
195 newpoints.append([x, y, 0])
196 j = 1
197 while j < sides:
198 t = angle * j
199 x = cos(t + startangle) * radius
200 y = sin(t + startangle) * radius
201 newpoints.append([x, y, 0])
202 j += 1
203 x = cos(endangle) * radius
204 y = sin(endangle) * radius
205 newpoints.append([x, y, 0])
207 return newpoints
210 # ------------------------------------------------------------
211 # Segment:
213 def SimpleSegment(sides=0, a=2.0, b=1.0, startangle=0.0, endangle=45.0):
214 newpoints = []
216 startangle = radians(startangle)
217 endangle = radians(endangle)
218 sides += 1
220 angle = (endangle - startangle) / sides
221 x = cos(startangle) * a
222 y = sin(startangle) * a
223 newpoints.append([x, y, 0])
224 j = 1
225 while j < sides:
226 t = angle * j
227 x = cos(t + startangle) * a
228 y = sin(t + startangle) * a
229 newpoints.append([x, y, 0])
230 j += 1
231 x = cos(endangle) * a
232 y = sin(endangle) * a
233 newpoints.append([x, y, 0])
235 x = cos(endangle) * b
236 y = sin(endangle) * b
237 newpoints.append([x, y, 0])
238 j = sides - 1
239 while j > 0:
240 t = angle * j
241 x = cos(t + startangle) * b
242 y = sin(t + startangle) * b
243 newpoints.append([x, y, 0])
244 j -= 1
245 x = cos(startangle) * b
246 y = sin(startangle) * b
247 newpoints.append([x, y, 0])
249 return newpoints
252 # ------------------------------------------------------------
253 # Rectangle:
255 def SimpleRectangle(width=2.0, length=2.0, rounded=0.0, center=True):
256 newpoints = []
258 r = rounded / 2
260 if center:
261 x = width / 2
262 y = length / 2
263 if rounded != 0.0:
264 newpoints.append([-x + r, y, 0.0])
265 newpoints.append([x - r, y, 0.0])
266 newpoints.append([x, y - r, 0.0])
267 newpoints.append([x, -y + r, 0.0])
268 newpoints.append([x - r, -y, 0.0])
269 newpoints.append([-x + r, -y, 0.0])
270 newpoints.append([-x, -y + r, 0.0])
271 newpoints.append([-x, y - r, 0.0])
272 else:
273 newpoints.append([-x, y, 0.0])
274 newpoints.append([x, y, 0.0])
275 newpoints.append([x, -y, 0.0])
276 newpoints.append([-x, -y, 0.0])
278 else:
279 x = width
280 y = length
281 if rounded != 0.0:
282 newpoints.append([r, y, 0.0])
283 newpoints.append([x - r, y, 0.0])
284 newpoints.append([x, y - r, 0.0])
285 newpoints.append([x, r, 0.0])
286 newpoints.append([x - r, 0.0, 0.0])
287 newpoints.append([r, 0.0, 0.0])
288 newpoints.append([0.0, r, 0.0])
289 newpoints.append([0.0, y - r, 0.0])
290 else:
291 newpoints.append([0.0, 0.0, 0.0])
292 newpoints.append([0.0, y, 0.0])
293 newpoints.append([x, y, 0.0])
294 newpoints.append([x, 0.0, 0.0])
296 return newpoints
299 # ------------------------------------------------------------
300 # Rhomb:
302 def SimpleRhomb(width=2.0, length=2.0, center=True):
303 newpoints = []
304 x = width / 2
305 y = length / 2
307 if center:
308 newpoints.append([-x, 0.0, 0.0])
309 newpoints.append([0.0, y, 0.0])
310 newpoints.append([x, 0.0, 0.0])
311 newpoints.append([0.0, -y, 0.0])
312 else:
313 newpoints.append([x, 0.0, 0.0])
314 newpoints.append([0.0, y, 0.0])
315 newpoints.append([x, length, 0.0])
316 newpoints.append([width, y, 0.0])
318 return newpoints
321 # ------------------------------------------------------------
322 # Polygon:
324 def SimplePolygon(sides=3, radius=1.0):
325 newpoints = []
326 angle = radians(360.0) / sides
327 j = 0
329 while j < sides:
330 t = angle * j
331 x = sin(t) * radius
332 y = cos(t) * radius
333 newpoints.append([x, y, 0.0])
334 j += 1
336 return newpoints
339 # ------------------------------------------------------------
340 # Polygon_ab:
342 def SimplePolygon_ab(sides=3, a=2.0, b=1.0):
343 newpoints = []
344 angle = radians(360.0) / sides
345 j = 0
347 while j < sides:
348 t = angle * j
349 x = sin(t) * a
350 y = cos(t) * b
351 newpoints.append([x, y, 0.0])
352 j += 1
354 return newpoints
357 # ------------------------------------------------------------
358 # Trapezoid:
360 def SimpleTrapezoid(a=2.0, b=1.0, h=1.0, center=True):
361 newpoints = []
362 x = a / 2
363 y = b / 2
364 r = h / 2
366 if center:
367 newpoints.append([-x, -r, 0.0])
368 newpoints.append([-y, r, 0.0])
369 newpoints.append([y, r, 0.0])
370 newpoints.append([x, -r, 0.0])
372 else:
373 newpoints.append([0.0, 0.0, 0.0])
374 newpoints.append([x - y, h, 0.0])
375 newpoints.append([x + y, h, 0.0])
376 newpoints.append([a, 0.0, 0.0])
378 return newpoints
381 # ------------------------------------------------------------
382 # get array of vertcoordinates according to splinetype
383 def vertsToPoints(Verts, splineType):
385 # main vars
386 vertArray = []
388 # array for BEZIER spline output (V3)
389 if splineType == 'BEZIER':
390 for v in Verts:
391 vertArray += v
393 # array for nonBEZIER output (V4)
394 else:
395 for v in Verts:
396 vertArray += v
397 if splineType == 'NURBS':
398 # for nurbs w=1
399 vertArray.append(1)
400 else:
401 # for poly w=0
402 vertArray.append(0)
403 return vertArray
406 # ------------------------------------------------------------
407 # Main Function
409 def main(context, self, use_enter_edit_mode):
410 # output splineType 'POLY' 'NURBS' 'BEZIER'
411 splineType = self.outputType
413 sides = abs(int((self.Simple_endangle - self.Simple_startangle) / 90))
415 # get verts
416 if self.Simple_Type == 'Point':
417 verts = SimplePoint()
419 if self.Simple_Type == 'Line':
420 verts = SimpleLine(self.location, self.Simple_endlocation)
422 if self.Simple_Type == 'Distance':
423 verts = SimpleDistance(self.Simple_length, self.Simple_center)
425 if self.Simple_Type == 'Angle':
426 verts = SimpleAngle(self.Simple_length, self.Simple_angle)
428 if self.Simple_Type == 'Circle':
429 if self.Simple_sides < 4:
430 self.Simple_sides = 4
431 if self.Simple_radius == 0:
432 return {'FINISHED'}
433 verts = SimpleCircle(self.Simple_sides, self.Simple_radius)
435 if self.Simple_Type == 'Ellipse':
436 verts = SimpleEllipse(self.Simple_a, self.Simple_b)
438 if self.Simple_Type == 'Arc':
439 if self.Simple_sides < sides:
440 self.Simple_sides = sides
441 if self.Simple_radius == 0:
442 return {'FINISHED'}
443 verts = SimpleArc(
444 self.Simple_sides, self.Simple_radius,
445 self.Simple_startangle, self.Simple_endangle
448 if self.Simple_Type == 'Sector':
449 if self.Simple_sides < sides:
450 self.Simple_sides = sides
451 if self.Simple_radius == 0:
452 return {'FINISHED'}
453 verts = SimpleSector(
454 self.Simple_sides, self.Simple_radius,
455 self.Simple_startangle, self.Simple_endangle
458 if self.Simple_Type == 'Segment':
459 if self.Simple_sides < sides:
460 self.Simple_sides = sides
461 if self.Simple_a == 0 or self.Simple_b == 0 or self.Simple_a == self.Simple_b:
462 return {'FINISHED'}
463 if self.Simple_a > self.Simple_b:
464 verts = SimpleSegment(
465 self.Simple_sides, self.Simple_a, self.Simple_b,
466 self.Simple_startangle, self.Simple_endangle
468 if self.Simple_a < self.Simple_b:
469 verts = SimpleSegment(
470 self.Simple_sides, self.Simple_b, self.Simple_a,
471 self.Simple_startangle, self.Simple_endangle
474 if self.Simple_Type == 'Rectangle':
475 verts = SimpleRectangle(
476 self.Simple_width, self.Simple_length,
477 self.Simple_rounded, self.Simple_center
480 if self.Simple_Type == 'Rhomb':
481 verts = SimpleRhomb(
482 self.Simple_width, self.Simple_length, self.Simple_center
485 if self.Simple_Type == 'Polygon':
486 if self.Simple_sides < 3:
487 self.Simple_sides = 3
488 verts = SimplePolygon(
489 self.Simple_sides, self.Simple_radius
492 if self.Simple_Type == 'Polygon_ab':
493 if self.Simple_sides < 3:
494 self.Simple_sides = 3
495 verts = SimplePolygon_ab(
496 self.Simple_sides, self.Simple_a, self.Simple_b
499 if self.Simple_Type == 'Trapezoid':
500 verts = SimpleTrapezoid(
501 self.Simple_a, self.Simple_b, self.Simple_h, self.Simple_center
504 # turn verts into array
505 vertArray = vertsToPoints(verts, splineType)
507 # create object
508 if bpy.context.mode == 'EDIT_CURVE':
510 Curve = context.active_object
511 newSpline = Curve.data.splines.new(type=splineType) # spline
512 else:
513 name = self.Simple_Type # Type as name
515 dataCurve = bpy.data.curves.new(name, type='CURVE') # curve data block
516 newSpline = dataCurve.splines.new(type=splineType) # spline
518 # create object with new Curve
519 Curve = object_utils.object_data_add(context, dataCurve, operator=self) # place in active scene
520 Curve.select_set(True)
522 for spline in Curve.data.splines:
523 if spline.type == 'BEZIER':
524 for point in spline.bezier_points:
525 point.select_control_point = False
526 point.select_left_handle = False
527 point.select_right_handle = False
528 else:
529 for point in spline.points:
530 point.select = False
532 # create spline from vertarray
533 all_points = []
534 if splineType == 'BEZIER':
535 newSpline.bezier_points.add(int(len(vertArray) * 0.33))
536 newSpline.bezier_points.foreach_set('co', vertArray)
537 for point in newSpline.bezier_points:
538 point.handle_right_type = self.handleType
539 point.handle_left_type = self.handleType
540 point.select_control_point = True
541 point.select_left_handle = True
542 point.select_right_handle = True
543 all_points.append(point)
544 else:
545 newSpline.points.add(int(len(vertArray) * 0.25 - 1))
546 newSpline.points.foreach_set('co', vertArray)
547 newSpline.use_endpoint_u = True
548 for point in newSpline.points:
549 all_points.append(point)
550 point.select = True
552 n = len(all_points)
554 d = 2 * 0.27606262
556 if splineType == 'BEZIER':
557 if self.Simple_Type == 'Circle' or self.Simple_Type == 'Arc' or \
558 self.Simple_Type == 'Sector' or self.Simple_Type == 'Segment' or \
559 self.Simple_Type == 'Ellipse':
561 for p in all_points:
562 p.handle_right_type = 'FREE'
563 p.handle_left_type = 'FREE'
565 if self.Simple_Type == 'Circle':
566 i = 0
567 for p1 in all_points:
568 if i != (n - 1):
569 p2 = all_points[i + 1]
570 u1 = asin(p1.co.y / self.Simple_radius)
571 u2 = asin(p2.co.y / self.Simple_radius)
572 if p1.co.x > 0 and p2.co.x < 0:
573 u1 = acos(p1.co.x / self.Simple_radius)
574 u2 = acos(p2.co.x / self.Simple_radius)
575 elif p1.co.x < 0 and p2.co.x > 0:
576 u1 = acos(p1.co.x / self.Simple_radius)
577 u2 = acos(p2.co.x / self.Simple_radius)
578 u = u2 - u1
579 if u < 0:
580 u = -u
581 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
582 v1 = Vector((-p1.co.y, p1.co.x, 0))
583 v1.normalize()
584 v2 = Vector((-p2.co.y, p2.co.x, 0))
585 v2.normalize()
586 vh1 = v1 * l
587 vh2 = v2 * l
588 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
589 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
590 p1.handle_right = v1
591 p2.handle_left = v2
592 if i == (n - 1):
593 p2 = all_points[0]
594 u1 = asin(p1.co.y / self.Simple_radius)
595 u2 = asin(p2.co.y / self.Simple_radius)
596 if p1.co.x > 0 and p2.co.x < 0:
597 u1 = acos(p1.co.x / self.Simple_radius)
598 u2 = acos(p2.co.x / self.Simple_radius)
599 elif p1.co.x < 0 and p2.co.x > 0:
600 u1 = acos(p1.co.x / self.Simple_radius)
601 u2 = acos(p2.co.x / self.Simple_radius)
602 u = u2 - u1
603 if u < 0:
604 u = -u
605 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
606 v1 = Vector((-p1.co.y, p1.co.x, 0))
607 v1.normalize()
608 v2 = Vector((-p2.co.y, p2.co.x, 0))
609 v2.normalize()
610 vh1 = v1 * l
611 vh2 = v2 * l
612 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
613 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
614 p1.handle_right = v1
615 p2.handle_left = v2
616 i += 1
618 if self.Simple_Type == 'Ellipse':
619 all_points[0].handle_right = Vector((self.Simple_a, self.Simple_b * d, 0))
620 all_points[0].handle_left = Vector((self.Simple_a, -self.Simple_b * d, 0))
621 all_points[1].handle_right = Vector((-self.Simple_a * d, self.Simple_b, 0))
622 all_points[1].handle_left = Vector((self.Simple_a * d, self.Simple_b, 0))
623 all_points[2].handle_right = Vector((-self.Simple_a, -self.Simple_b * d, 0))
624 all_points[2].handle_left = Vector((-self.Simple_a, self.Simple_b * d, 0))
625 all_points[3].handle_right = Vector((self.Simple_a * d, -self.Simple_b, 0))
626 all_points[3].handle_left = Vector((-self.Simple_a * d, -self.Simple_b, 0))
628 if self.Simple_Type == 'Arc':
629 i = 0
630 for p1 in all_points:
631 if i != (n - 1):
632 p2 = all_points[i + 1]
633 u1 = asin(p1.co.y / self.Simple_radius)
634 u2 = asin(p2.co.y / self.Simple_radius)
635 if p1.co.x > 0 and p2.co.x < 0:
636 u1 = acos(p1.co.x / self.Simple_radius)
637 u2 = acos(p2.co.x / self.Simple_radius)
638 elif p1.co.x < 0 and p2.co.x > 0:
639 u1 = acos(p1.co.x / self.Simple_radius)
640 u2 = acos(p2.co.x / self.Simple_radius)
641 u = u2 - u1
642 if u < 0:
643 u = -u
644 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
645 v1 = Vector((-p1.co.y, p1.co.x, 0))
646 v1.normalize()
647 v2 = Vector((-p2.co.y, p2.co.x, 0))
648 v2.normalize()
649 vh1 = v1 * l
650 vh2 = v2 * l
651 if self.Simple_startangle < self.Simple_endangle:
652 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
653 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
654 p1.handle_right = v1
655 p2.handle_left = v2
656 else:
657 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
658 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
659 p1.handle_right = v1
660 p2.handle_left = v2
661 i += 1
662 all_points[0].handle_left_type = 'VECTOR'
663 all_points[-1].handle_right_type = 'VECTOR'
665 if self.Simple_Type == 'Sector':
666 i = 0
667 for p1 in all_points:
668 if i == 0:
669 p1.handle_right_type = 'VECTOR'
670 p1.handle_left_type = 'VECTOR'
671 elif i != (n - 1):
672 p2 = all_points[i + 1]
673 u1 = asin(p1.co.y / self.Simple_radius)
674 u2 = asin(p2.co.y / self.Simple_radius)
675 if p1.co.x > 0 and p2.co.x < 0:
676 u1 = acos(p1.co.x / self.Simple_radius)
677 u2 = acos(p2.co.x / self.Simple_radius)
678 elif p1.co.x < 0 and p2.co.x > 0:
679 u1 = acos(p1.co.x / self.Simple_radius)
680 u2 = acos(p2.co.x / self.Simple_radius)
681 u = u2 - u1
682 if u < 0:
683 u = -u
684 l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius
685 v1 = Vector((-p1.co.y, p1.co.x, 0))
686 v1.normalize()
687 v2 = Vector((-p2.co.y, p2.co.x, 0))
688 v2.normalize()
689 vh1 = v1 * l
690 vh2 = v2 * l
691 if self.Simple_startangle < self.Simple_endangle:
692 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
693 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
694 p1.handle_right = v1
695 p2.handle_left = v2
696 else:
697 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
698 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
699 p1.handle_right = v1
700 p2.handle_left = v2
701 i += 1
702 all_points[0].handle_left_type = 'VECTOR'
703 all_points[0].handle_right_type = 'VECTOR'
704 all_points[1].handle_left_type = 'VECTOR'
705 all_points[-1].handle_right_type = 'VECTOR'
707 if self.Simple_Type == 'Segment':
708 i = 0
709 if self.Simple_a > self.Simple_b:
710 Segment_a = self.Simple_a
711 Segment_b = self.Simple_b
712 if self.Simple_a < self.Simple_b:
713 Segment_b = self.Simple_a
714 Segment_a = self.Simple_b
715 for p1 in all_points:
716 if i < (n / 2 - 1):
717 p2 = all_points[i + 1]
718 u1 = asin(p1.co.y / Segment_a)
719 u2 = asin(p2.co.y / Segment_a)
720 if p1.co.x > 0 and p2.co.x < 0:
721 u1 = acos(p1.co.x / Segment_a)
722 u2 = acos(p2.co.x / Segment_a)
723 elif p1.co.x < 0 and p2.co.x > 0:
724 u1 = acos(p1.co.x / Segment_a)
725 u2 = acos(p2.co.x / Segment_a)
726 u = u2 - u1
727 if u < 0:
728 u = -u
729 l = 4 / 3 * tan(1 / 4 * u) * Segment_a
730 v1 = Vector((-p1.co.y, p1.co.x, 0))
731 v1.normalize()
732 v2 = Vector((-p2.co.y, p2.co.x, 0))
733 v2.normalize()
734 vh1 = v1 * l
735 vh2 = v2 * l
736 if self.Simple_startangle < self.Simple_endangle:
737 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
738 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
739 p1.handle_right = v1
740 p2.handle_left = v2
741 else:
742 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
743 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
744 p1.handle_right = v1
745 p2.handle_left = v2
746 elif i != (n / 2 - 1) and i != (n - 1):
747 p2 = all_points[i + 1]
748 u1 = asin(p1.co.y / Segment_b)
749 u2 = asin(p2.co.y / Segment_b)
750 if p1.co.x > 0 and p2.co.x < 0:
751 u1 = acos(p1.co.x / Segment_b)
752 u2 = acos(p2.co.x / Segment_b)
753 elif p1.co.x < 0 and p2.co.x > 0:
754 u1 = acos(p1.co.x / Segment_b)
755 u2 = acos(p2.co.x / Segment_b)
756 u = u2 - u1
757 if u < 0:
758 u = -u
759 l = 4 / 3 * tan(1 / 4 * u) * Segment_b
760 v1 = Vector((-p1.co.y, p1.co.x, 0))
761 v1.normalize()
762 v2 = Vector((-p2.co.y, p2.co.x, 0))
763 v2.normalize()
764 vh1 = v1 * l
765 vh2 = v2 * l
766 if self.Simple_startangle < self.Simple_endangle:
767 v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1
768 v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2
769 p1.handle_right = v1
770 p2.handle_left = v2
771 else:
772 v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1
773 v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2
774 p1.handle_right = v1
775 p2.handle_left = v2
777 i += 1
778 all_points[0].handle_left_type = 'VECTOR'
779 all_points[n - 1].handle_right_type = 'VECTOR'
780 all_points[int(n / 2) - 1].handle_right_type = 'VECTOR'
781 all_points[int(n / 2)].handle_left_type = 'VECTOR'
783 # set newSpline Options
784 newSpline.use_cyclic_u = self.use_cyclic_u
785 newSpline.use_endpoint_u = self.endp_u
786 newSpline.order_u = self.order_u
788 # set curve Options
789 Curve.data.dimensions = self.shape
790 Curve.data.use_path = True
791 if self.shape == '3D':
792 Curve.data.fill_mode = 'FULL'
793 else:
794 Curve.data.fill_mode = 'BOTH'
796 # move and rotate spline in edit mode
797 if bpy.context.mode == 'EDIT_CURVE':
798 if self.align == "WORLD":
799 location = self.location - context.active_object.location
800 bpy.ops.transform.translate(value = location, orient_type='GLOBAL')
801 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X', orient_type='GLOBAL')
802 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y', orient_type='GLOBAL')
803 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z', orient_type='GLOBAL')
805 elif self.align == "VIEW":
806 bpy.ops.transform.translate(value = self.location)
807 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
808 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
809 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
811 elif self.align == "CURSOR":
812 location = context.active_object.location
813 self.location = bpy.context.scene.cursor.location - location
814 self.rotation = bpy.context.scene.cursor.rotation_euler
816 bpy.ops.transform.translate(value = self.location)
817 bpy.ops.transform.rotate(value = self.rotation[0], orient_axis = 'X')
818 bpy.ops.transform.rotate(value = self.rotation[1], orient_axis = 'Y')
819 bpy.ops.transform.rotate(value = self.rotation[2], orient_axis = 'Z')
822 def menu(self, context):
823 oper1 = self.layout.operator(Simple.bl_idname, text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
824 oper1.Simple_Type = "Angle"
825 oper1.use_cyclic_u = False
827 oper2 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_THICKNESS")
828 oper2.Simple_Type = "Arc"
829 oper2.use_cyclic_u = False
831 oper3 = self.layout.operator(Simple.bl_idname, text="Circle", icon="ANTIALIASED")
832 oper3.Simple_Type = "Circle"
833 oper3.use_cyclic_u = True
835 oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="DRIVER_DISTANCE")
836 oper4.Simple_Type = "Distance"
837 oper4.use_cyclic_u = False
839 oper5 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MESH_TORUS")
840 oper5.Simple_Type = "Ellipse"
841 oper5.use_cyclic_u = True
843 oper6 = self.layout.operator(Simple.bl_idname, text="Line", icon="MOD_SIMPLIFY")
844 oper6.Simple_Type = "Line"
845 oper6.use_cyclic_u = False
846 oper6.shape = '3D'
848 oper7 = self.layout.operator(Simple.bl_idname, text="Point", icon="LAYER_ACTIVE")
849 oper7.Simple_Type = "Point"
850 oper7.use_cyclic_u = False
852 oper8 = self.layout.operator(Simple.bl_idname, text="Polygon", icon="SEQ_CHROMA_SCOPE")
853 oper8.Simple_Type = "Polygon"
854 oper8.use_cyclic_u = True
856 oper9 = self.layout.operator(Simple.bl_idname, text="Polygon ab", icon="SEQ_CHROMA_SCOPE")
857 oper9.Simple_Type = "Polygon_ab"
858 oper9.use_cyclic_u = True
860 oper10 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MESH_PLANE")
861 oper10.Simple_Type = "Rectangle"
862 oper10.use_cyclic_u = True
864 oper11 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="DECORATE_ANIMATE")
865 oper11.Simple_Type = "Rhomb"
866 oper11.use_cyclic_u = True
868 oper12 = self.layout.operator(Simple.bl_idname, text="Sector", icon="CON_SHRINKWRAP")
869 oper12.Simple_Type = "Sector"
870 oper12.use_cyclic_u = True
872 oper13 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_SIMPLEDEFORM")
873 oper13.Simple_Type = "Segment"
874 oper13.use_cyclic_u = True
876 oper14 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_EDGESPLIT")
877 oper14.Simple_Type = "Trapezoid"
878 oper14.use_cyclic_u = True
880 # ------------------------------------------------------------
881 # Simple operator
883 class Simple(Operator, object_utils.AddObjectHelper):
884 bl_idname = "curve.simple"
885 bl_label = "Simple Curve"
886 bl_description = "Construct a Simple Curve"
887 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
889 # change properties
890 Simple : BoolProperty(
891 name="Simple",
892 default=True,
893 description="Simple Curve"
895 Simple_Change : BoolProperty(
896 name="Change",
897 default=False,
898 description="Change Simple Curve"
900 Simple_Delete : StringProperty(
901 name="Delete",
902 description="Delete Simple Curve"
904 # general properties
905 Types = [('Point', "Point", "Construct a Point"),
906 ('Line', "Line", "Construct a Line"),
907 ('Distance', "Distance", "Construct a two point Distance"),
908 ('Angle', "Angle", "Construct an Angle"),
909 ('Circle', "Circle", "Construct a Circle"),
910 ('Ellipse', "Ellipse", "Construct an Ellipse"),
911 ('Arc', "Arc", "Construct an Arc"),
912 ('Sector', "Sector", "Construct a Sector"),
913 ('Segment', "Segment", "Construct a Segment"),
914 ('Rectangle', "Rectangle", "Construct a Rectangle"),
915 ('Rhomb', "Rhomb", "Construct a Rhomb"),
916 ('Polygon', "Polygon", "Construct a Polygon"),
917 ('Polygon_ab', "Polygon ab", "Construct a Polygon ab"),
918 ('Trapezoid', "Trapezoid", "Construct a Trapezoid")
920 Simple_Type : EnumProperty(
921 name="Type",
922 description="Form of Curve to create",
923 items=Types
925 # Line properties
926 Simple_endlocation : FloatVectorProperty(
927 name="",
928 description="End location",
929 default=(2.0, 2.0, 2.0),
930 subtype='TRANSLATION'
932 # Trapezoid properties
933 Simple_a : FloatProperty(
934 name="Side a",
935 default=2.0,
936 min=0.0, soft_min=0.0,
937 unit='LENGTH',
938 description="a side Value"
940 Simple_b : FloatProperty(
941 name="Side b",
942 default=1.0,
943 min=0.0, soft_min=0.0,
944 unit='LENGTH',
945 description="b side Value"
947 Simple_h : FloatProperty(
948 name="Height",
949 default=1.0,
950 unit='LENGTH',
951 description="Height of the Trapezoid - distance between a and b"
953 Simple_angle : FloatProperty(
954 name="Angle",
955 default=45.0,
956 description="Angle"
958 Simple_startangle : FloatProperty(
959 name="Start angle",
960 default=0.0,
961 min=-360.0, soft_min=-360.0,
962 max=360.0, soft_max=360.0,
963 description="Start angle"
965 Simple_endangle : FloatProperty(
966 name="End angle",
967 default=45.0,
968 min=-360.0, soft_min=-360.0,
969 max=360.0, soft_max=360.0,
970 description="End angle"
972 Simple_sides : IntProperty(
973 name="Sides",
974 default=3,
975 min=0, soft_min=0,
976 description="Sides"
978 Simple_radius : FloatProperty(
979 name="Radius",
980 default=1.0,
981 min=0.0, soft_min=0.0,
982 unit='LENGTH',
983 description="Radius"
985 Simple_center : BoolProperty(
986 name="Length center",
987 default=True,
988 description="Length center"
991 Angle_types = [('Degrees', "Degrees", "Use Degrees"),
992 ('Radians', "Radians", "Use Radians")]
993 Simple_degrees_or_radians : EnumProperty(
994 name="Degrees or radians",
995 description="Degrees or radians",
996 items=Angle_types
998 # Rectangle properties
999 Simple_width : FloatProperty(
1000 name="Width",
1001 default=2.0,
1002 min=0.0, soft_min=0,
1003 unit='LENGTH',
1004 description="Width"
1006 Simple_length : FloatProperty(
1007 name="Length",
1008 default=2.0,
1009 min=0.0, soft_min=0.0,
1010 unit='LENGTH',
1011 description="Length"
1013 Simple_rounded : FloatProperty(
1014 name="Rounded",
1015 default=0.0,
1016 min=0.0, soft_min=0.0,
1017 unit='LENGTH',
1018 description="Rounded corners"
1020 # Curve Options
1021 shapeItems = [
1022 ('2D', "2D", "2D shape Curve"),
1023 ('3D', "3D", "3D shape Curve")]
1024 shape : EnumProperty(
1025 name="2D / 3D",
1026 items=shapeItems,
1027 description="2D or 3D Curve"
1029 outputType : EnumProperty(
1030 name="Output splines",
1031 description="Type of splines to output",
1032 items=[
1033 ('POLY', "Poly", "Poly Spline type"),
1034 ('NURBS', "Nurbs", "Nurbs Spline type"),
1035 ('BEZIER', "Bezier", "Bezier Spline type")],
1036 default='BEZIER'
1038 use_cyclic_u : BoolProperty(
1039 name="Cyclic",
1040 default=True,
1041 description="make curve closed"
1043 endp_u : BoolProperty(
1044 name="Use endpoint u",
1045 default=True,
1046 description="stretch to endpoints"
1048 order_u : IntProperty(
1049 name="Order u",
1050 default=4,
1051 min=2, soft_min=2,
1052 max=6, soft_max=6,
1053 description="Order of nurbs spline"
1055 handleType : EnumProperty(
1056 name="Handle type",
1057 default='VECTOR',
1058 description="Bezier handles type",
1059 items=[
1060 ('VECTOR', "Vector", "Vector type Bezier handles"),
1061 ('AUTO', "Auto", "Automatic type Bezier handles")]
1063 edit_mode : BoolProperty(
1064 name="Show in edit mode",
1065 default=True,
1066 description="Show in edit mode"
1069 def draw(self, context):
1070 layout = self.layout
1072 # general options
1073 col = layout.column()
1074 col.prop(self, "Simple_Type")
1076 l = 0
1077 s = 0
1079 if self.Simple_Type == 'Line':
1080 box = layout.box()
1081 col = box.column(align=True)
1082 col.label(text=self.Simple_Type + " Options:")
1083 col.prop(self, "Simple_endlocation")
1084 v = Vector(self.Simple_endlocation) - Vector(self.location)
1085 l = v.length
1087 if self.Simple_Type == 'Distance':
1088 box = layout.box()
1089 col = box.column(align=True)
1090 col.label(text=self.Simple_Type + " Options:")
1091 col.prop(self, "Simple_length")
1092 col.prop(self, "Simple_center")
1093 l = self.Simple_length
1095 if self.Simple_Type == 'Angle':
1096 box = layout.box()
1097 col = box.column(align=True)
1098 col.label(text=self.Simple_Type + " Options:")
1099 col.prop(self, "Simple_length")
1100 col.prop(self, "Simple_angle")
1102 if self.Simple_Type == 'Circle':
1103 box = layout.box()
1104 col = box.column(align=True)
1105 col.label(text=self.Simple_Type + " Options:")
1106 col.prop(self, "Simple_sides")
1107 col.prop(self, "Simple_radius")
1109 l = 2 * pi * abs(self.Simple_radius)
1110 s = pi * self.Simple_radius * self.Simple_radius
1112 if self.Simple_Type == 'Ellipse':
1113 box = layout.box()
1114 col = box.column(align=True)
1115 col.label(text=self.Simple_Type + " Options:")
1116 col.prop(self, "Simple_a", text="Radius a")
1117 col.prop(self, "Simple_b", text="Radius b")
1119 l = pi * (3 * (self.Simple_a + self.Simple_b) -
1120 sqrt((3 * self.Simple_a + self.Simple_b) *
1121 (self.Simple_a + 3 * self.Simple_b)))
1123 s = pi * abs(self.Simple_b) * abs(self.Simple_a)
1125 if self.Simple_Type == 'Arc':
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 * (self.Simple_endangle - self.Simple_startangle) / 180)
1140 if self.Simple_Type == 'Sector':
1141 box = layout.box()
1142 col = box.column(align=True)
1143 col.label(text=self.Simple_Type + " Options:")
1144 col.prop(self, "Simple_sides")
1145 col.prop(self, "Simple_radius")
1147 col = box.column(align=True)
1148 col.prop(self, "Simple_startangle")
1149 col.prop(self, "Simple_endangle")
1150 #row = layout.row()
1151 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1153 l = abs(pi * self.Simple_radius *
1154 (self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2
1156 s = pi * self.Simple_radius * self.Simple_radius * \
1157 abs(self.Simple_endangle - self.Simple_startangle) / 360
1159 if self.Simple_Type == 'Segment':
1160 box = layout.box()
1161 col = box.column(align=True)
1162 col.label(text=self.Simple_Type + " Options:")
1163 col.prop(self, "Simple_sides")
1164 col.prop(self, "Simple_a", text="Radius a")
1165 col.prop(self, "Simple_b", text="Radius b")
1167 col = box.column(align=True)
1168 col.prop(self, "Simple_startangle")
1169 col.prop(self, "Simple_endangle")
1171 #row = layout.row()
1172 #row.prop(self, "Simple_degrees_or_radians", expand=True)
1174 la = abs(pi * self.Simple_a * (self.Simple_endangle - self.Simple_startangle) / 180)
1175 lb = abs(pi * self.Simple_b * (self.Simple_endangle - self.Simple_startangle) / 180)
1176 l = abs(self.Simple_a - self.Simple_b) * 2 + la + lb
1178 sa = pi * self.Simple_a * self.Simple_a * \
1179 abs(self.Simple_endangle - self.Simple_startangle) / 360
1181 sb = pi * self.Simple_b * self.Simple_b * \
1182 abs(self.Simple_endangle - self.Simple_startangle) / 360
1184 s = abs(sa - sb)
1186 if self.Simple_Type == 'Rectangle':
1187 box = layout.box()
1188 col = box.column(align=True)
1189 col.label(text=self.Simple_Type + " Options:")
1190 col.prop(self, "Simple_width")
1191 col.prop(self, "Simple_length")
1192 col.prop(self, "Simple_rounded")
1194 box.prop(self, "Simple_center")
1195 l = 2 * abs(self.Simple_width) + 2 * abs(self.Simple_length)
1196 s = abs(self.Simple_width) * abs(self.Simple_length)
1198 if self.Simple_Type == 'Rhomb':
1199 box = layout.box()
1200 col = box.column(align=True)
1201 col.label(text=self.Simple_Type + " Options:")
1202 col.prop(self, "Simple_width")
1203 col.prop(self, "Simple_length")
1204 col.prop(self, "Simple_center")
1206 g = hypot(self.Simple_width / 2, self.Simple_length / 2)
1207 l = 4 * g
1208 s = self.Simple_width * self.Simple_length / 2
1210 if self.Simple_Type == 'Polygon':
1211 box = layout.box()
1212 col = box.column(align=True)
1213 col.label(text=self.Simple_Type + " Options:")
1214 col.prop(self, "Simple_sides")
1215 col.prop(self, "Simple_radius")
1217 if self.Simple_Type == 'Polygon_ab':
1218 box = layout.box()
1219 col = box.column(align=True)
1220 col.label(text="Polygon ab Options:")
1221 col.prop(self, "Simple_sides")
1222 col.prop(self, "Simple_a")
1223 col.prop(self, "Simple_b")
1225 if self.Simple_Type == 'Trapezoid':
1226 box = layout.box()
1227 col = box.column(align=True)
1228 col.label(text=self.Simple_Type + " Options:")
1229 col.prop(self, "Simple_a")
1230 col.prop(self, "Simple_b")
1231 col.prop(self, "Simple_h")
1233 box.prop(self, "Simple_center")
1234 g = hypot(self.Simple_h, (self.Simple_a - self.Simple_b) / 2)
1235 l = self.Simple_a + self.Simple_b + g * 2
1236 s = (abs(self.Simple_a) + abs(self.Simple_b)) / 2 * self.Simple_h
1238 row = layout.row()
1239 row.prop(self, "shape", expand=True)
1241 # output options
1242 col = layout.column()
1243 col.label(text="Output Curve Type:")
1244 col.row().prop(self, "outputType", expand=True)
1246 if self.outputType == 'NURBS':
1247 col.prop(self, "order_u")
1248 elif self.outputType == 'BEZIER':
1249 col.row().prop(self, 'handleType', expand=True)
1251 col = layout.column()
1252 col.row().prop(self, "use_cyclic_u", expand=True)
1254 col = layout.column()
1255 col.row().prop(self, "edit_mode", expand=True)
1257 col = layout.column()
1258 # AddObjectHelper props
1259 col.prop(self, "align")
1260 col.prop(self, "location")
1261 col.prop(self, "rotation")
1263 if l != 0 or s != 0:
1264 box = layout.box()
1265 box.label(text="Statistics:", icon="INFO")
1266 if l != 0:
1267 l_str = str(round(l, 4))
1268 box.label(text="Length: " + l_str)
1269 if s != 0:
1270 s_str = str(round(s, 4))
1271 box.label(text="Area: " + s_str)
1273 @classmethod
1274 def poll(cls, context):
1275 return context.scene is not None
1277 def execute(self, context):
1279 # turn off 'Enter Edit Mode'
1280 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
1281 bpy.context.preferences.edit.use_enter_edit_mode = False
1283 # main function
1284 main(context, self, use_enter_edit_mode)
1286 if use_enter_edit_mode:
1287 bpy.ops.object.mode_set(mode = 'EDIT')
1289 # restore pre operator state
1290 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
1292 if self.edit_mode:
1293 bpy.ops.object.mode_set(mode = 'EDIT')
1294 else:
1295 bpy.ops.object.mode_set(mode = 'OBJECT')
1297 return {'FINISHED'}
1299 def invoke(self, context, event):
1301 self.execute(context)
1303 return {'FINISHED'}
1305 # Register
1306 classes = [
1307 Simple,
1310 def register():
1311 from bpy.utils import register_class
1312 for cls in classes:
1313 register_class(cls)
1315 bpy.types.VIEW3D_MT_curve_add.append(menu)
1317 def unregister():
1318 from bpy.utils import unregister_class
1319 for cls in reversed(classes):
1320 unregister_class(cls)
1322 bpy.types.VIEW3D_MT_curve_add.remove(menu)
1324 if __name__ == "__main__":
1325 register()