Newer
Older
# SPDX-License-Identifier: GPL-2.0-or-later
bl_info = {
"name": "New Braid",
"author": "Jared Forsyth <github.com/jaredly>",
"version": (1, 0, 3),
"blender": (2, 80, 0),
"location": "View3D > Add > Mesh > New Braid",
"description": "Adds a new Braid",
"warning": "",
"doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/extra_objects.html",
"category": "Add Mesh",
}
"""
import bpy
from bpy.props import (
FloatProperty,
IntProperty,
BoolProperty,
)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from math import (
sin, cos,
pi,
)
def angle_point(center, angle, distance):
cx, cy = center
x = cos(angle) * distance
y = sin(angle) * distance
return x + cx, y + cy
def flat_hump(strands, mx=1, my=1, mz=1, resolution=2):
num = 4 * resolution
dy = 2 * pi / num
dz = 2 * pi * (strands - 1) / num
for i in range(num):
x = i * mx
y = cos(i * dy) * my
z = sin(i * dz) * mz
yield x, y, z
def circle_hump(pos, strands, humps, radius=1, mr=1, mz=.2, resolution=2):
num = 5 * resolution
dt = 2 * pi / humps * strands / num
dr = 2 * pi * (strands - 1) / num
dz = 2 * pi / num
t0 = 2 * pi / humps * pos
for i in range(num):
x, y = angle_point((0, 0), i * dt + t0, radius + sin(i * dr) * mr)
z = cos(i * dz) * mz
yield x, y, z
def make_strands(strands, humps, radius=1, mr=1, mz=.2, resolution=2):
positions = [0 for x in range(humps)]
last = None
lines = []
at = 0
while 0 in positions:
if positions[at]:
at = positions.index(0)
last = None
hump = list(circle_hump(at, strands, humps, radius, mr, mz, resolution))
if last is None:
last = hump
lines.append(last)
else:
last.extend(hump)
positions[at] = 1
at += strands
at %= humps
return lines
def poly_line(curve, points, join=True, type='NURBS'):
polyline = curve.splines.new(type)
polyline.points.add(len(points) - 1)
for num in range(len(points)):
polyline.points[num].co = (points[num]) + (1,)
polyline.order_u = len(polyline.points) - 1
if join:
polyline.use_cyclic_u = True
def poly_lines(objname, curvename, lines, bevel=None, joins=False, ctype='NURBS'):
curve = bpy.data.curves.new(name=curvename, type='CURVE')
curve.dimensions = '3D'
Spivak Vladimir (cwolf3d)
committed
curve.fill_mode = 'FULL'
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
obj = bpy.data.objects.new(objname, curve)
obj.location = (0, 0, 0) # object origin
for i, line in enumerate(lines):
poly_line(curve, line, joins if type(joins) == bool else joins[i], type=ctype)
if bevel:
curve.bevel_object = bpy.data.objects[bevel]
return obj
def nurbs_circle(name, w, h):
pts = [(-w / 2, 0, 0), (0, -h / 2, 0), (w / 2, 0, 0), (0, h / 2, 0)]
return poly_lines(name, name + '_curve', [pts], joins=True)
def star_pts(r=1, ir=None, points=5, center=(0, 0)):
"""
Create points for a star. They are 2d - z is always zero
r: the outer radius
ir: the inner radius
"""
if not ir:
ir = r / 5
pts = []
dt = pi * 2 / points
for i in range(points):
t = i * dt
ti = (i + .5) * dt
pts.append(angle_point(center, t, r) + (0,))
pts.append(angle_point(center, ti, ir) + (0,))
return pts
def defaultCircle(w=.6):
circle = nurbs_circle('braid_circle', w, w)
return circle
def defaultStar():
star = poly_lines('star', 'staz', [tuple(star_pts(points=5, r=.5, ir=.05))], type='NURBS')
return star
def awesome_braid(strands=3, sides=5, bevel='braid_circle', pointy=False, **kwds):
lines = make_strands(strands, sides, **kwds)
types = {True: 'POLY', False: 'NURBS'}[pointy]
return poly_lines('Braid', 'Braid_c', lines, bevel=bevel, joins=True, ctype=types)
class Braid(Operator):
bl_label = "New Braid"
bl_description = ("Construct a new Braid\n"
"Creates two objects - the hidden one is used as the Bevel control")
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
name="Strands",
description="Number of Strands",
min=2, max=100,
default=3
)
name="Sides",
description="Number of Knot sides",
min=2, max=100,
default=5
)
name="Radius",
description="Increase / decrease the diameter in X,Y axis",
default=1
)
name="Thickness",
description="The ratio between inner and outside diameters",
default=.3
)
name="Bevel Depth",
description="Individual strand diameter (similar to Curve's Bevel depth)",
default=.3,
min=.01, max=10
)
name="Width",
description="Stretch the Braids along the Z axis",
default=.2
)
name="Bevel Resolution",
description="Resolution of the Created curve\n"
"Increasing this value, will produce heavy geometry",
min=1,
max=100, soft_max=24,
default=2
)
name="Pointy",
description="Switch between round and sharp corners",
default=False
)
Spivak Vladimir (cwolf3d)
committed
edit_mode : BoolProperty(
name="Show in edit mode",
default=True,
description="Show in edit mode"
)
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column(align=True)
col.prop(self, "strands")
col.prop(self, "sides")
col = box.column(align=True)
col.prop(self, "radius")
col.prop(self, "thickness")
col.prop(self, "width")
col = box.column()
col.prop(self, "pointy")
box = layout.box()
col = box.column(align=True)
col.label(text="Geometry Options:")
col.prop(self, "strandsize")
col.prop(self, "resolution")
Spivak Vladimir (cwolf3d)
committed
col = layout.column()
col.row().prop(self, "edit_mode", expand=True)
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
circle = defaultCircle(self.strandsize)
context.scene.collection.objects.link(circle)
braid = awesome_braid(
self.strands, self.sides,
bevel=circle.name,
pointy=self.pointy,
radius=self.radius,
mr=self.thickness,
mz=self.width,
resolution=self.resolution
)
base = context.scene.collection.objects.link(braid)
for ob in context.scene.objects:
braid.select_set(True)
bpy.context.view_layer.objects.active = braid
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
Spivak Vladimir (cwolf3d)
committed
if self.edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
else:
bpy.ops.object.mode_set(mode = 'OBJECT')
def register():
bpy.utils.register_class(Braid)
def unregister():
bpy.utils.unregister_class(Braid)
if __name__ == "__main__":
register()