1 # gpl: author Jared Forsyth <github.com/jaredly>
6 "author": "Jared Forsyth <github.com/jaredly>",
9 "location": "View3D > Add > Mesh > New Braid",
10 "description": "Adds a new Braid",
13 "category": "Add Mesh",
18 from bpy
.props
import (
23 from bpy
.types
import Operator
30 def angle_point(center
, angle
, distance
):
32 x
= cos(angle
) * distance
33 y
= sin(angle
) * distance
37 def flat_hump(strands
, mx
=1, my
=1, mz
=1, resolution
=2):
40 dz
= 2 * pi
* (strands
- 1) / num
49 def circle_hump(pos
, strands
, humps
, radius
=1, mr
=1, mz
=.2, resolution
=2):
51 dt
= 2 * pi
/ humps
* strands
/ num
52 dr
= 2 * pi
* (strands
- 1) / num
54 t0
= 2 * pi
/ humps
* pos
57 x
, y
= angle_point((0, 0), i
* dt
+ t0
, radius
+ sin(i
* dr
) * mr
)
63 def make_strands(strands
, humps
, radius
=1, mr
=1, mz
=.2, resolution
=2):
64 positions
= [0 for x
in range(humps
)]
71 at
= positions
.index(0)
73 hump
= list(circle_hump(at
, strands
, humps
, radius
, mr
, mz
, resolution
))
86 def poly_line(curve
, points
, join
=True, type='NURBS'):
87 polyline
= curve
.splines
.new(type)
88 polyline
.points
.add(len(points
) - 1)
89 for num
in range(len(points
)):
90 polyline
.points
[num
].co
= (points
[num
]) + (1,)
92 polyline
.order_u
= len(polyline
.points
) - 1
94 polyline
.use_cyclic_u
= True
97 def poly_lines(objname
, curvename
, lines
, bevel
=None, joins
=False, ctype
='NURBS'):
98 curve
= bpy
.data
.curves
.new(name
=curvename
, type='CURVE')
99 curve
.dimensions
= '3D'
100 curve
.fill_mode
= 'FULL'
102 obj
= bpy
.data
.objects
.new(objname
, curve
)
103 obj
.location
= (0, 0, 0) # object origin
105 for i
, line
in enumerate(lines
):
106 poly_line(curve
, line
, joins
if type(joins
) == bool else joins
[i
], type=ctype
)
109 curve
.bevel_object
= bpy
.data
.objects
[bevel
]
113 def nurbs_circle(name
, w
, h
):
114 pts
= [(-w
/ 2, 0, 0), (0, -h
/ 2, 0), (w
/ 2, 0, 0), (0, h
/ 2, 0)]
115 return poly_lines(name
, name
+ '_curve', [pts
], joins
=True)
118 def star_pts(r
=1, ir
=None, points
=5, center
=(0, 0)):
120 Create points for a star. They are 2d - z is always zero
129 for i
in range(points
):
132 pts
.append(angle_point(center
, t
, r
) + (0,))
133 pts
.append(angle_point(center
, ti
, ir
) + (0,))
137 def defaultCircle(w
=.6):
138 circle
= nurbs_circle('braid_circle', w
, w
)
139 circle
.hide_select
= True
144 star
= poly_lines('star', 'staz', [tuple(star_pts(points
=5, r
=.5, ir
=.05))], type='NURBS')
145 star
.hide_select
= True
149 def awesome_braid(strands
=3, sides
=5, bevel
='braid_circle', pointy
=False, **kwds
):
150 lines
= make_strands(strands
, sides
, **kwds
)
151 types
= {True: 'POLY', False: 'NURBS'}[pointy
]
152 return poly_lines('Braid', 'Braid_c', lines
, bevel
=bevel
, joins
=True, ctype
=types
)
155 class Braid(Operator
):
156 bl_idname
= "curve.add_braid"
157 bl_label
= "New Braid"
158 bl_description
= ("Construct a new Braid\n"
159 "Creates two objects - the hidden one is used as the Bevel control")
160 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
162 strands
: IntProperty(
164 description
="Number of Strands",
170 description
="Number of Knot sides",
174 radius
: FloatProperty(
176 description
="Increase / decrease the diameter in X,Y axis",
179 thickness
: FloatProperty(
181 description
="The ratio between inner and outside diameters",
184 strandsize
: FloatProperty(
186 description
="Individual strand diameter (similar to Curve's Bevel depth)",
190 width
: FloatProperty(
192 description
="Stretch the Braids along the Z axis",
195 resolution
: IntProperty(
196 name
="Bevel Resolution",
197 description
="Resolution of the Created curve\n"
198 "Increasing this value, will produce heavy geometry",
200 max=100, soft_max
=24,
203 pointy
: BoolProperty(
205 description
="Switch between round and sharp corners",
208 edit_mode
: BoolProperty(
209 name
="Show in edit mode",
211 description
="Show in edit mode"
214 def draw(self
, context
):
218 col
= box
.column(align
=True)
219 col
.label(text
="Settings:")
220 col
.prop(self
, "strands")
221 col
.prop(self
, "sides")
223 col
= box
.column(align
=True)
224 col
.prop(self
, "radius")
225 col
.prop(self
, "thickness")
226 col
.prop(self
, "width")
229 col
.prop(self
, "pointy")
232 col
= box
.column(align
=True)
233 col
.label(text
="Geometry Options:")
234 col
.prop(self
, "strandsize")
235 col
.prop(self
, "resolution")
237 col
= layout
.column()
238 col
.row().prop(self
, "edit_mode", expand
=True)
240 def execute(self
, context
):
241 # turn off 'Enter Edit Mode'
242 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
243 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
245 circle
= defaultCircle(self
.strandsize
)
246 context
.scene
.collection
.objects
.link(circle
)
247 braid
= awesome_braid(
248 self
.strands
, self
.sides
,
254 resolution
=self
.resolution
256 base
= context
.scene
.collection
.objects
.link(braid
)
258 for ob
in context
.scene
.objects
:
260 braid
.select_set(True)
261 bpy
.context
.view_layer
.objects
.active
= braid
263 if use_enter_edit_mode
:
264 bpy
.ops
.object.mode_set(mode
= 'EDIT')
266 # restore pre operator state
267 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
270 bpy
.ops
.object.mode_set(mode
= 'EDIT')
272 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
278 bpy
.utils
.register_class(Braid
)
282 bpy
.utils
.unregister_class(Braid
)
285 if __name__
== "__main__":