1# ##### BEGIN GPL LICENSE BLOCK #####
2#
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.
7#
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.
12#
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.
16#
17# ##### END GPL LICENSE BLOCK #####
18
19bl_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",
30}
31
32
33# ------------------------------------------------------------
34
35import bpy
36from bpy_extras import object_utils
37from bpy.types import (
38        Operator,
39        Menu,
40        Panel,
41        PropertyGroup,
42        )
43from bpy.props import (
44        BoolProperty,
45        EnumProperty,
46        FloatProperty,
47        FloatVectorProperty,
48        IntProperty,
49        StringProperty,
50        PointerProperty,
51        )
52from mathutils import (
53        Vector,
54        Matrix,
55        )
56from math import (
57        sin, asin, sqrt,
58        acos, cos, pi,
59        radians, tan,
60        hypot,
61        )
62# from bpy_extras.object_utils import *
63
64
65# ------------------------------------------------------------
66# Point:
67
68def SimplePoint():
69    newpoints = []
70
71    newpoints.append([0.0, 0.0, 0.0])
72
73    return newpoints
74
75
76# ------------------------------------------------------------
77# Line:
78
79def SimpleLine(c1=[0.0, 0.0, 0.0], c2=[2.0, 2.0, 2.0]):
80    newpoints = []
81
82    c3 = Vector(c2) - Vector(c1)
83    newpoints.append([0.0, 0.0, 0.0])
84    newpoints.append([c3[0], c3[1], c3[2]])
85
86    return newpoints
87
88
89# ------------------------------------------------------------
90# Angle:
91
92def SimpleAngle(length=1.0, angle=45.0):
93    newpoints = []
94
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])
99
100    return newpoints
101
102
103# ------------------------------------------------------------
104# Distance:
105
106def SimpleDistance(length=1.0, center=True):
107    newpoints = []
108
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])
115
116    return newpoints
117
118
119# ------------------------------------------------------------
120# Circle:
121
122def SimpleCircle(sides=4, radius=1.0):
123    newpoints = []
124
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
135
136    return newpoints
137
138
139# ------------------------------------------------------------
140# Ellipse:
141
142def SimpleEllipse(a=2.0, b=1.0):
143    newpoints = []
144
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])
149
150    return newpoints
151
152
153# ------------------------------------------------------------
154# Arc:
155
156def SimpleArc(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
157    newpoints = []
158
159    startangle = radians(startangle)
160    endangle = radians(endangle)
161    sides += 1
162
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])
177
178    return newpoints
179
180
181# ------------------------------------------------------------
182# Sector:
183
184def SimpleSector(sides=0, radius=1.0, startangle=0.0, endangle=45.0):
185    newpoints = []
186
187    startangle = radians(startangle)
188    endangle = radians(endangle)
189    sides += 1
190
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])
206
207    return newpoints
208
209
210# ------------------------------------------------------------
211# Segment:
212
213def SimpleSegment(sides=0, a=2.0, b=1.0, startangle=0.0, endangle=45.0):
214    newpoints = []
215
216    startangle = radians(startangle)
217    endangle = radians(endangle)
218    sides += 1
219
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])
234
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])
248
249    return newpoints
250
251
252# ------------------------------------------------------------
253# Rectangle:
254
255def SimpleRectangle(width=2.0, length=2.0, rounded=0.0, center=True):
256    newpoints = []
257
258    r = rounded / 2
259
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])
277
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])
295
296    return newpoints
297
298
299# ------------------------------------------------------------
300# Rhomb:
301
302def SimpleRhomb(width=2.0, length=2.0, center=True):
303    newpoints = []
304    x = width / 2
305    y = length / 2
306
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])
317
318    return newpoints
319
320
321# ------------------------------------------------------------
322# Polygon:
323
324def SimplePolygon(sides=3, radius=1.0):
325    newpoints = []
326    angle = radians(360.0) / sides
327    j = 0
328
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
335
336    return newpoints
337
338
339# ------------------------------------------------------------
340# Polygon_ab:
341
342def SimplePolygon_ab(sides=3, a=2.0, b=1.0):
343    newpoints = []
344    angle = radians(360.0) / sides
345    j = 0
346
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
353
354    return newpoints
355
356
357# ------------------------------------------------------------
358# Trapezoid:
359
360def 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
365
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])
371
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])
377
378    return newpoints
379
380
381# ------------------------------------------------------------
382# get array of vertcoordinates according to splinetype
383def vertsToPoints(Verts, splineType):
384
385    # main vars
386    vertArray = []
387
388    # array for BEZIER spline output (V3)
389    if splineType == 'BEZIER':
390        for v in Verts:
391            vertArray += v
392
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
404
405
406# ------------------------------------------------------------
407# Main Function
408
409def main(context, self, use_enter_edit_mode):
410    # output splineType 'POLY' 'NURBS' 'BEZIER'
411    splineType = self.outputType
412
413    sides = abs(int((self.Simple_endangle - self.Simple_startangle) / 90))
414
415    # get verts
416    if self.Simple_Type == 'Point':
417        verts = SimplePoint()
418
419    if self.Simple_Type == 'Line':
420        verts = SimpleLine(self.location, self.Simple_endlocation)
421
422    if self.Simple_Type == 'Distance':
423        verts = SimpleDistance(self.Simple_length, self.Simple_center)
424
425    if self.Simple_Type == 'Angle':
426        verts = SimpleAngle(self.Simple_length, self.Simple_angle)
427
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)
434
435    if self.Simple_Type == 'Ellipse':
436        verts = SimpleEllipse(self.Simple_a, self.Simple_b)
437
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
446                    )
447
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
456                    )
457
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
467                    )
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
472                    )
473
474    if self.Simple_Type == 'Rectangle':
475        verts = SimpleRectangle(
476                    self.Simple_width, self.Simple_length,
477                    self.Simple_rounded, self.Simple_center
478                    )
479
480    if self.Simple_Type == 'Rhomb':
481        verts = SimpleRhomb(
482                    self.Simple_width, self.Simple_length, self.Simple_center
483                    )
484
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
490                    )
491
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
497                    )
498
499    if self.Simple_Type == 'Trapezoid':
500        verts = SimpleTrapezoid(
501                    self.Simple_a, self.Simple_b, self.Simple_h, self.Simple_center
502                    )
503
504    # turn verts into array
505    vertArray = vertsToPoints(verts, splineType)
506
507    # create object
508    if bpy.context.mode == 'EDIT_CURVE':
509
510        Curve = context.active_object
511        newSpline = Curve.data.splines.new(type=splineType)          # spline
512    else:
513        name = self.Simple_Type  # Type as name
514
515        dataCurve = bpy.data.curves.new(name, type='CURVE')  # curve data block
516        newSpline = dataCurve.splines.new(type=splineType)          # spline
517
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)
521
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
531
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
551
552    n = len(all_points)
553
554    d = 2 * 0.27606262
555
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':
560
561            for p in all_points:
562                p.handle_right_type = 'FREE'
563                p.handle_left_type = 'FREE'
564
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
617
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))
627
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'
664
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'
706
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
776
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'
782
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
787
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'
795
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')
804
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')
810
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
815
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')
820
821
822def 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
826
827    oper2 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_THICKNESS")
828    oper2.Simple_Type = "Arc"
829    oper2.use_cyclic_u = False
830
831    oper3 = self.layout.operator(Simple.bl_idname, text="Circle", icon="ANTIALIASED")
832    oper3.Simple_Type = "Circle"
833    oper3.use_cyclic_u = True
834
835    oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="DRIVER_DISTANCE")
836    oper4.Simple_Type = "Distance"
837    oper4.use_cyclic_u = False
838
839    oper5 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MESH_TORUS")
840    oper5.Simple_Type = "Ellipse"
841    oper5.use_cyclic_u = True
842
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'
847
848    oper7 = self.layout.operator(Simple.bl_idname, text="Point", icon="LAYER_ACTIVE")
849    oper7.Simple_Type = "Point"
850    oper7.use_cyclic_u = False
851
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
855
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
859
860    oper10 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MESH_PLANE")
861    oper10.Simple_Type = "Rectangle"
862    oper10.use_cyclic_u = True
863
864    oper11 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="DECORATE_ANIMATE")
865    oper11.Simple_Type = "Rhomb"
866    oper11.use_cyclic_u = True
867
868    oper12 = self.layout.operator(Simple.bl_idname, text="Sector", icon="CON_SHRINKWRAP")
869    oper12.Simple_Type = "Sector"
870    oper12.use_cyclic_u = True
871
872    oper13 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_SIMPLEDEFORM")
873    oper13.Simple_Type = "Segment"
874    oper13.use_cyclic_u = True
875
876    oper14 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_EDGESPLIT")
877    oper14.Simple_Type = "Trapezoid"
878    oper14.use_cyclic_u = True
879
880# ------------------------------------------------------------
881# Simple operator
882
883class 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'}
888
889    # change properties
890    Simple : BoolProperty(
891            name="Simple",
892            default=True,
893            description="Simple Curve"
894            )
895    Simple_Change : BoolProperty(
896            name="Change",
897            default=False,
898            description="Change Simple Curve"
899            )
900    Simple_Delete : StringProperty(
901            name="Delete",
902            description="Delete Simple Curve"
903            )
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")
919            ]
920    Simple_Type : EnumProperty(
921            name="Type",
922            description="Form of Curve to create",
923            items=Types
924            )
925    # Line properties
926    Simple_endlocation : FloatVectorProperty(
927            name="",
928            description="End location",
929            default=(2.0, 2.0, 2.0),
930            subtype='TRANSLATION'
931            )
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"
939            )
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"
946            )
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"
952            )
953    Simple_angle : FloatProperty(
954            name="Angle",
955            default=45.0,
956            description="Angle"
957            )
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"
964            )
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"
971            )
972    Simple_sides : IntProperty(
973            name="Sides",
974            default=3,
975            min=0, soft_min=0,
976            description="Sides"
977            )
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"
984            )
985    Simple_center : BoolProperty(
986            name="Length center",
987            default=True,
988            description="Length center"
989            )
990
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
997            )
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"
1005            )
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"
1012            )
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"
1019            )
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"
1028            )
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'
1037            )
1038    use_cyclic_u : BoolProperty(
1039            name="Cyclic",
1040            default=True,
1041            description="make curve closed"
1042            )
1043    endp_u : BoolProperty(
1044            name="Use endpoint u",
1045            default=True,
1046            description="stretch to endpoints"
1047            )
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"
1054            )
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")]
1062            )
1063    edit_mode : BoolProperty(
1064            name="Show in edit mode",
1065            default=True,
1066            description="Show in edit mode"
1067            )
1068
1069    def draw(self, context):
1070        layout = self.layout
1071
1072        # general options
1073        col = layout.column()
1074        col.prop(self, "Simple_Type")
1075
1076        l = 0
1077        s = 0
1078
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
1086
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
1094
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")
1101
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")
1108
1109            l = 2 * pi * abs(self.Simple_radius)
1110            s = pi * self.Simple_radius * self.Simple_radius
1111
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")
1118
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)))
1122
1123            s = pi * abs(self.Simple_b) * abs(self.Simple_a)
1124
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")
1131
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)
1137
1138            l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180)
1139
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")
1146
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)
1152
1153            l = abs(pi * self.Simple_radius *
1154                   (self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2
1155
1156            s = pi * self.Simple_radius * self.Simple_radius * \
1157                abs(self.Simple_endangle - self.Simple_startangle) / 360
1158
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")
1166
1167            col = box.column(align=True)
1168            col.prop(self, "Simple_startangle")
1169            col.prop(self, "Simple_endangle")
1170
1171            #row = layout.row()
1172            #row.prop(self, "Simple_degrees_or_radians", expand=True)
1173
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
1177
1178            sa = pi * self.Simple_a * self.Simple_a * \
1179                abs(self.Simple_endangle - self.Simple_startangle) / 360
1180
1181            sb = pi * self.Simple_b * self.Simple_b * \
1182                abs(self.Simple_endangle - self.Simple_startangle) / 360
1183
1184            s = abs(sa - sb)
1185
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")
1193
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)
1197
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")
1205
1206            g = hypot(self.Simple_width / 2, self.Simple_length / 2)
1207            l = 4 * g
1208            s = self.Simple_width * self.Simple_length / 2
1209
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")
1216
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")
1224
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")
1232
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
1237
1238        row = layout.row()
1239        row.prop(self, "shape", expand=True)
1240
1241        # output options
1242        col = layout.column()
1243        col.label(text="Output Curve Type:")
1244        col.row().prop(self, "outputType", expand=True)
1245
1246        if self.outputType == 'NURBS':
1247            col.prop(self, "order_u")
1248        elif self.outputType == 'BEZIER':
1249            col.row().prop(self, 'handleType', expand=True)
1250
1251        col = layout.column()
1252        col.row().prop(self, "use_cyclic_u", expand=True)
1253
1254        col = layout.column()
1255        col.row().prop(self, "edit_mode", expand=True)
1256
1257        col = layout.column()
1258        # AddObjectHelper props
1259        col.prop(self, "align")
1260        col.prop(self, "location")
1261        col.prop(self, "rotation")
1262
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)
1272
1273    @classmethod
1274    def poll(cls, context):
1275        return context.scene is not None
1276
1277    def execute(self, context):
1278
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
1282
1283        # main function
1284        main(context, self, use_enter_edit_mode)
1285
1286        if use_enter_edit_mode:
1287            bpy.ops.object.mode_set(mode = 'EDIT')
1288
1289        # restore pre operator state
1290        bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
1291
1292        if self.edit_mode:
1293            bpy.ops.object.mode_set(mode = 'EDIT')
1294        else:
1295            bpy.ops.object.mode_set(mode = 'OBJECT')
1296
1297        return {'FINISHED'}
1298
1299    def invoke(self, context, event):
1300
1301        self.execute(context)
1302
1303        return {'FINISHED'}
1304
1305# Register
1306classes = [
1307    Simple,
1308]
1309
1310def register():
1311    from bpy.utils import register_class
1312    for cls in classes:
1313        register_class(cls)
1314
1315    bpy.types.VIEW3D_MT_curve_add.append(menu)
1316
1317def unregister():
1318    from bpy.utils import unregister_class
1319    for cls in reversed(classes):
1320        unregister_class(cls)
1321
1322    bpy.types.VIEW3D_MT_curve_add.remove(menu)
1323
1324if __name__ == "__main__":
1325    register()
1326