Newer
Older
default=1.0,
min=0.0, soft_min=0.0,
unit='LENGTH',
description="Radius"
)
name="Length center",
default=True,
description="Length center"
)
Angle_types = [('Degrees', "Degrees", "Use Degrees"),
('Radians', "Radians", "Use Radians")]
Simple_degrees_or_radians : EnumProperty(
name="Degrees or radians",
description="Degrees or radians",
items=Angle_types
)
name="Width",
default=2.0,
min=0.0, soft_min=0,
unit='LENGTH',
description="Width"
)
name="Length",
default=2.0,
min=0.0, soft_min=0.0,
unit='LENGTH',
description="Length"
)
Simple_rounded : FloatProperty(
name="Rounded",
default=0.0,
min=0.0, soft_min=0.0,
unit='LENGTH',
description="Rounded corners"
)
# Curve Options
shapeItems = [
('2D', "2D", "2D shape Curve"),
('3D', "3D", "3D shape Curve")]
name="2D / 3D",
items=shapeItems,
description="2D or 3D Curve"
)
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
outputType : EnumProperty(
name="Output splines",
description="Type of splines to output",
items=[
('POLY', "Poly", "Poly Spline type"),
('NURBS', "Nurbs", "Nurbs Spline type"),
('BEZIER', "Bezier", "Bezier Spline type")],
default='BEZIER'
)
use_cyclic_u : BoolProperty(
name="Cyclic",
default=True,
description="make curve closed"
)
endp_u : BoolProperty(
name="Use endpoint u",
default=True,
description="stretch to endpoints"
)
order_u : IntProperty(
name="Order u",
default=4,
min=2, soft_min=2,
max=6, soft_max=6,
description="Order of nurbs spline"
)
handleType : EnumProperty(
name="Handle type",
default='VECTOR',
description="Bezier handles type",
items=[
('VECTOR', "Vector", "Vector type Bezier handles"),
('AUTO', "Auto", "Automatic type Bezier handles")]
)
def draw(self, context):
layout = self.layout
# general options
col = layout.column()
col.prop(self, "Simple_Type")
l = 0
s = 0
if self.Simple_Type == 'Line':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_endlocation")
v = Vector(self.Simple_endlocation) - Vector(self.Simple_startlocation)
l = v.length
if self.Simple_Type == 'Distance':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_length")
col.prop(self, "Simple_center")
l = self.Simple_length
if self.Simple_Type == 'Angle':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_length")
col.prop(self, "Simple_angle")
#row = layout.row()
#row.prop(self, "Simple_degrees_or_radians", expand=True)
if self.Simple_Type == 'Circle':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_sides")
col.prop(self, "Simple_radius")
l = 2 * pi * abs(self.Simple_radius)
s = pi * self.Simple_radius * self.Simple_radius
if self.Simple_Type == 'Ellipse':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_a", text="Radius a")
col.prop(self, "Simple_b", text="Radius b")
l = pi * (3 * (self.Simple_a + self.Simple_b) -
sqrt((3 * self.Simple_a + self.Simple_b) *
(self.Simple_a + 3 * self.Simple_b)))
s = pi * abs(self.Simple_b) * abs(self.Simple_a)
if self.Simple_Type == 'Arc':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_sides")
col.prop(self, "Simple_radius")
col = box.column(align=True)
col.prop(self, "Simple_startangle")
col.prop(self, "Simple_endangle")
#row = layout.row()
#row.prop(self, "Simple_degrees_or_radians", expand=True)
l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180)
if self.Simple_Type == 'Sector':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_sides")
col.prop(self, "Simple_radius")
col = box.column(align=True)
col.prop(self, "Simple_startangle")
col.prop(self, "Simple_endangle")
#row = layout.row()
#row.prop(self, "Simple_degrees_or_radians", expand=True)
l = abs(pi * self.Simple_radius *
(self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2
s = pi * self.Simple_radius * self.Simple_radius * \
abs(self.Simple_endangle - self.Simple_startangle) / 360
if self.Simple_Type == 'Segment':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_sides")
col.prop(self, "Simple_a", text="Radius a")
col.prop(self, "Simple_b", text="Radius b")
col = box.column(align=True)
col.prop(self, "Simple_startangle")
col.prop(self, "Simple_endangle")
#row = layout.row()
#row.prop(self, "Simple_degrees_or_radians", expand=True)
la = abs(pi * self.Simple_a * (self.Simple_endangle - self.Simple_startangle) / 180)
lb = abs(pi * self.Simple_b * (self.Simple_endangle - self.Simple_startangle) / 180)
l = abs(self.Simple_a - self.Simple_b) * 2 + la + lb
sa = pi * self.Simple_a * self.Simple_a * \
abs(self.Simple_endangle - self.Simple_startangle) / 360
sb = pi * self.Simple_b * self.Simple_b * \
abs(self.Simple_endangle - self.Simple_startangle) / 360
s = abs(sa - sb)
if self.Simple_Type == 'Rectangle':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_width")
col.prop(self, "Simple_length")
col.prop(self, "Simple_rounded")
box.prop(self, "Simple_center")
l = 2 * abs(self.Simple_width) + 2 * abs(self.Simple_length)
s = abs(self.Simple_width) * abs(self.Simple_length)
if self.Simple_Type == 'Rhomb':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_width")
col.prop(self, "Simple_length")
col.prop(self, "Simple_center")
g = hypot(self.Simple_width / 2, self.Simple_length / 2)
l = 4 * g
s = self.Simple_width * self.Simple_length / 2
if self.Simple_Type == 'Polygon':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_sides")
col.prop(self, "Simple_radius")
if self.Simple_Type == 'Polygon_ab':
box = layout.box()
col = box.column(align=True)
col.label(text="Polygon ab Options:")
col.prop(self, "Simple_sides")
col.prop(self, "Simple_a")
col.prop(self, "Simple_b")
if self.Simple_Type == 'Trapezoid':
box = layout.box()
col = box.column(align=True)
col.label(text=self.Simple_Type + " Options:")
col.prop(self, "Simple_a")
col.prop(self, "Simple_b")
col.prop(self, "Simple_h")
box.prop(self, "Simple_center")
g = hypot(self.Simple_h, (self.Simple_a - self.Simple_b) / 2)
l = self.Simple_a + self.Simple_b + g * 2
s = (abs(self.Simple_a) + abs(self.Simple_b)) / 2 * self.Simple_h
row = layout.row()
row.prop(self, "shape", expand=True)
# output options
col = layout.column()
col.label(text="Output Curve Type:")
col.row().prop(self, "outputType", expand=True)
if self.outputType == 'NURBS':
col.prop(self, "order_u")
elif self.outputType == 'BEZIER':
col.row().prop(self, 'handleType', expand=True)
col = layout.column()
col.row().prop(self, "use_cyclic_u", expand=True)
box.prop(self, "Simple_startlocation")
box.prop(self, "Simple_rotation_euler")
if l != 0 or s != 0:
box = layout.box()
box.label(text="Statistics:", icon="INFO")
if l != 0:
l_str = str(round(l, 4))
box.label(text="Length: " + l_str)
if s != 0:
s_str = str(round(s, 4))
box.label(text="Area: " + s_str)
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
# main function
self.align_matrix = align_matrix(context, self.Simple_startlocation)
main(context, self, self.align_matrix, use_enter_edit_mode)
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
# ------------------------------------------------------------
# Fillet
class BezierPointsFillet(Operator):
bl_idname = "curve.bezier_points_fillet"
bl_label = "Bezier points Fillet"
bl_description = "Bezier points Fillet"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
name="Radius",
default=0.25,
unit='LENGTH',
description="Radius"
)
Types = [('Round', "Round", "Round"),
('Chamfer', "Chamfer", "Chamfer")]
name="Type",
description="Fillet type",
items=Types
)
def draw(self, context):
layout = self.layout
# general options
col = layout.column()
col.prop(self, "Fillet_radius")
col.prop(self, "Fillet_Type", expand=True)
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
# main function
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='EDIT')
spline = bpy.context.object.data.splines.active
bpy.ops.curve.spline_type_set(type='BEZIER')
bpy.ops.curve.handle_type_set(type='VECTOR')
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
n = 0
ii = []
for p in spline.bezier_points:
if p.select_control_point:
ii.append(n)
n += 1
else:
n += 1
if n > 2:
jn = 0
for j in ii:
j += jn
selected_all = [p for p in spline.bezier_points]
bpy.ops.curve.select_all(action='DESELECT')
if j != 0 and j != n - 1:
selected_all[j].select_control_point = True
selected_all[j + 1].select_control_point = True
bpy.ops.curve.subdivide()
selected_all = [p for p in spline.bezier_points]
selected4 = [selected_all[j - 1], selected_all[j],
selected_all[j + 1], selected_all[j + 2]]
jn += 1
n += 1
elif j == 0:
selected_all[j].select_control_point = True
selected_all[j + 1].select_control_point = True
bpy.ops.curve.subdivide()
selected_all = [p for p in spline.bezier_points]
selected4 = [selected_all[n], selected_all[0],
selected_all[1], selected_all[2]]
jn += 1
n += 1
elif j == n - 1:
selected_all[j].select_control_point = True
selected_all[j - 1].select_control_point = True
bpy.ops.curve.subdivide()
selected_all = [p for p in spline.bezier_points]
selected4 = [selected_all[0], selected_all[n],
selected_all[n - 1], selected_all[n - 2]]
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
selected4[2].co = selected4[1].co
s1 = Vector(selected4[0].co) - Vector(selected4[1].co)
s2 = Vector(selected4[3].co) - Vector(selected4[2].co)
s1.normalize()
s11 = Vector(selected4[1].co) + s1 * self.Fillet_radius
selected4[1].co = s11
s2.normalize()
s22 = Vector(selected4[2].co) + s2 * self.Fillet_radius
selected4[2].co = s22
if self.Fillet_Type == 'Round':
if j != n - 1:
selected4[2].handle_right_type = 'VECTOR'
selected4[1].handle_left_type = 'VECTOR'
selected4[1].handle_right_type = 'ALIGNED'
selected4[2].handle_left_type = 'ALIGNED'
else:
selected4[1].handle_right_type = 'VECTOR'
selected4[2].handle_left_type = 'VECTOR'
selected4[2].handle_right_type = 'ALIGNED'
selected4[1].handle_left_type = 'ALIGNED'
if self.Fillet_Type == 'Chamfer':
selected4[2].handle_right_type = 'VECTOR'
selected4[1].handle_left_type = 'VECTOR'
selected4[1].handle_right_type = 'VECTOR'
selected4[2].handle_left_type = 'VECTOR'
return {'FINISHED'}
def subdivide_cubic_bezier(p1, p2, p3, p4, t):
p12 = (p2 - p1) * t + p1
p23 = (p3 - p2) * t + p2
p34 = (p4 - p3) * t + p3
p123 = (p23 - p12) * t + p12
p234 = (p34 - p23) * t + p23
p1234 = (p234 - p123) * t + p123
return [p12, p123, p1234, p234, p34]
# ------------------------------------------------------------
# BezierDivide Operator
class BezierDivide(Operator):
bl_idname = "curve.bezier_spline_divide"
bl_label = "Bezier Spline Divide"
bl_description = "Bezier Divide (enters edit mode) for Fillet Curves"
bl_options = {'REGISTER', 'UNDO'}
# align_matrix for the invoke
name="t (0% - 100%)",
default=50.0,
min=0.0, soft_min=0.0,
max=100.0, soft_max=100.0,
description="t (0% - 100%)"
)
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
# main function
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='EDIT')
spline = bpy.context.object.data.splines.active
bpy.ops.curve.spline_type_set(type='BEZIER')
Spivak Vladimir (cwolf3d)
committed
n = 0
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
ii = []
for p in spline.bezier_points:
if p.select_control_point:
ii.append(n)
n += 1
else:
n += 1
if n > 2:
jn = 0
for j in ii:
selected_all = [p for p in spline.bezier_points]
bpy.ops.curve.select_all(action='DESELECT')
if (j in ii) and (j + 1 in ii):
selected_all[j + jn].select_control_point = True
selected_all[j + 1 + jn].select_control_point = True
h = subdivide_cubic_bezier(
selected_all[j + jn].co, selected_all[j + jn].handle_right,
selected_all[j + 1 + jn].handle_left, selected_all[j + 1 + jn].co, self.Bezier_t / 100
)
bpy.ops.curve.subdivide(1)
selected_all = [p for p in spline.bezier_points]
selected_all[j + jn].handle_right_type = 'FREE'
selected_all[j + jn].handle_right = h[0]
selected_all[j + 1 + jn].co = h[2]
selected_all[j + 1 + jn].handle_left_type = 'FREE'
selected_all[j + 1 + jn].handle_left = h[1]
selected_all[j + 1 + jn].handle_right_type = 'FREE'
selected_all[j + 1 + jn].handle_right = h[3]
selected_all[j + 2 + jn].handle_left_type = 'FREE'
selected_all[j + 2 + jn].handle_left = h[4]
jn += 1
if j == n - 1 and (0 in ii) and spline.use_cyclic_u:
selected_all[j + jn].select_control_point = True
selected_all[0].select_control_point = True
h = subdivide_cubic_bezier(
selected_all[j + jn].co, selected_all[j + jn].handle_right,
selected_all[0].handle_left, selected_all[0].co, self.Bezier_t / 100
)
bpy.ops.curve.subdivide(1)
selected_all = [p for p in spline.bezier_points]
selected_all[j + jn].handle_right_type = 'FREE'
selected_all[j + jn].handle_right = h[0]
selected_all[j + 1 + jn].co = h[2]
selected_all[j + 1 + jn].handle_left_type = 'FREE'
selected_all[j + 1 + jn].handle_left = h[1]
selected_all[j + 1 + jn].handle_right_type = 'FREE'
selected_all[j + 1 + jn].handle_right = h[3]
selected_all[0].handle_left_type = 'FREE'
selected_all[0].handle_left = h[4]
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
# ------------------------------------------------------------
# CurveScaleReset Operator
class CurveScaleReset(Operator):
bl_idname = "curve.scale_reset"
bl_label = "Curve Scale Reset"
bl_description = "Curve Scale Reset"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
# main function
oldCurve = context.active_object
oldCurveName = oldCurve.name
bpy.ops.object.duplicate_move(OBJECT_OT_duplicate=None, TRANSFORM_OT_translate=None)
newCurve = context.active_object
newCurve.data.splines.clear()
newCurve.scale = (1.0, 1.0, 1.0)
oldCurve.select_set(True)
newCurve.select_set(True)
bpy.context.view_layer.objects.active = newCurve
bpy.ops.object.join()
joinCurve = context.active_object
joinCurve.name = oldCurveName
return {'FINISHED'}
classes = [
Simple,
BezierDivide,
BezierPointsFillet,
CurveScaleReset
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.VIEW3D_MT_curve_add.append(menu)
bpy.types.VIEW3D_MT_edit_curve_context_menu.prepend(Simple_curve_edit_menu)
bpy.types.VIEW3D_MT_object_context_menu.prepend(Simple_curve_object_menu)
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
bpy.types.VIEW3D_MT_curve_add.remove(menu)
bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(Simple_curve_edit_menu)
bpy.types.VIEW3D_MT_object_context_menu.remove(Simple_curve_object_menu)
if __name__ == "__main__":
register()