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