Import images as planes: use Principled BSDF for emission mode
[blender-addons.git] / add_curve_extra_objects / add_curve_braid.py
blob5f52d455a2a98e948cd261174efd9d458930fffe
1 # gpl: author Jared Forsyth <github.com/jaredly>
3 """
4 bl_info = {
5 "name": "New Braid",
6 "author": "Jared Forsyth <github.com/jaredly>",
7 "version": (1, 0, 3),
8 "blender": (2, 80, 0),
9 "location": "View3D > Add > Mesh > New Braid",
10 "description": "Adds a new Braid",
11 "warning": "",
12 "doc_url": "",
13 "category": "Add Mesh",
15 """
17 import bpy
18 from bpy.props import (
19 FloatProperty,
20 IntProperty,
21 BoolProperty,
23 from bpy.types import Operator
24 from math import (
25 sin, cos,
26 pi,
30 def angle_point(center, angle, distance):
31 cx, cy = center
32 x = cos(angle) * distance
33 y = sin(angle) * distance
34 return x + cx, y + cy
37 def flat_hump(strands, mx=1, my=1, mz=1, resolution=2):
38 num = 4 * resolution
39 dy = 2 * pi / num
40 dz = 2 * pi * (strands - 1) / num
41 for i in range(num):
42 x = i * mx
43 y = cos(i * dy) * my
44 z = sin(i * dz) * mz
46 yield x, y, z
49 def circle_hump(pos, strands, humps, radius=1, mr=1, mz=.2, resolution=2):
50 num = 5 * resolution
51 dt = 2 * pi / humps * strands / num
52 dr = 2 * pi * (strands - 1) / num
53 dz = 2 * pi / num
54 t0 = 2 * pi / humps * pos
56 for i in range(num):
57 x, y = angle_point((0, 0), i * dt + t0, radius + sin(i * dr) * mr)
58 z = cos(i * dz) * mz
60 yield x, y, z
63 def make_strands(strands, humps, radius=1, mr=1, mz=.2, resolution=2):
64 positions = [0 for x in range(humps)]
65 last = None
66 lines = []
67 at = 0
69 while 0 in positions:
70 if positions[at]:
71 at = positions.index(0)
72 last = None
73 hump = list(circle_hump(at, strands, humps, radius, mr, mz, resolution))
74 if last is None:
75 last = hump
76 lines.append(last)
77 else:
78 last.extend(hump)
79 positions[at] = 1
80 at += strands
81 at %= humps
83 return lines
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
93 if join:
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)
108 if bevel:
109 curve.bevel_object = bpy.data.objects[bevel]
110 return obj
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
122 r: the outer radius
123 ir: the inner radius
125 if not ir:
126 ir = r / 5
127 pts = []
128 dt = pi * 2 / points
129 for i in range(points):
130 t = i * dt
131 ti = (i + .5) * dt
132 pts.append(angle_point(center, t, r) + (0,))
133 pts.append(angle_point(center, ti, ir) + (0,))
134 return pts
137 def defaultCircle(w=.6):
138 circle = nurbs_circle('braid_circle', w, w)
139 circle.hide_select = True
140 return circle
143 def defaultStar():
144 star = poly_lines('star', 'staz', [tuple(star_pts(points=5, r=.5, ir=.05))], type='NURBS')
145 star.hide_select = True
146 return star
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(
163 name="Strands",
164 description="Number of Strands",
165 min=2, max=100,
166 default=3
168 sides : IntProperty(
169 name="Sides",
170 description="Number of Knot sides",
171 min=2, max=100,
172 default=5
174 radius : FloatProperty(
175 name="Radius",
176 description="Increase / decrease the diameter in X,Y axis",
177 default=1
179 thickness : FloatProperty(
180 name="Thickness",
181 description="The ratio between inner and outside diameters",
182 default=.3
184 strandsize : FloatProperty(
185 name="Bevel Depth",
186 description="Individual strand diameter (similar to Curve's Bevel depth)",
187 default=.3,
188 min=.01, max=10
190 width : FloatProperty(
191 name="Width",
192 description="Stretch the Braids along the Z axis",
193 default=.2
195 resolution : IntProperty(
196 name="Bevel Resolution",
197 description="Resolution of the Created curve\n"
198 "Increasing this value, will produce heavy geometry",
199 min=1,
200 max=100, soft_max=24,
201 default=2
203 pointy : BoolProperty(
204 name="Pointy",
205 description="Switch between round and sharp corners",
206 default=False
208 edit_mode : BoolProperty(
209 name="Show in edit mode",
210 default=True,
211 description="Show in edit mode"
214 def draw(self, context):
215 layout = self.layout
217 box = layout.box()
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")
228 col = box.column()
229 col.prop(self, "pointy")
231 box = layout.box()
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,
249 bevel=circle.name,
250 pointy=self.pointy,
251 radius=self.radius,
252 mr=self.thickness,
253 mz=self.width,
254 resolution=self.resolution
256 base = context.scene.collection.objects.link(braid)
258 for ob in context.scene.objects:
259 ob.select_set(False)
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
269 if self.edit_mode:
270 bpy.ops.object.mode_set(mode = 'EDIT')
271 else:
272 bpy.ops.object.mode_set(mode = 'OBJECT')
274 return {'FINISHED'}
277 def register():
278 bpy.utils.register_class(Braid)
281 def unregister():
282 bpy.utils.unregister_class(Braid)
285 if __name__ == "__main__":
286 register()