-
Aaron Carlisle authored
Also remove text referring to the old "Create" panel. See T86650
Aaron Carlisle authoredAlso remove text referring to the old "Create" panel. See T86650
add_curve_braid.py 7.85 KiB
# gpl: author Jared Forsyth <github.com/jaredly>
"""
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,
)
from bpy.types import Operator
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'
curve.fill_mode = 'FULL'
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)
circle.hide_select = True
return circle
def defaultStar():
star = poly_lines('star', 'staz', [tuple(star_pts(points=5, r=.5, ir=.05))], type='NURBS')
star.hide_select = True
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_idname = "curve.add_braid"
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'}
strands : IntProperty(
name="Strands",
description="Number of Strands",
min=2, max=100,
default=3
)
sides : IntProperty(
name="Sides",
description="Number of Knot sides",
min=2, max=100,
default=5
)
radius : FloatProperty(
name="Radius",
description="Increase / decrease the diameter in X,Y axis",
default=1
)
thickness : FloatProperty(
name="Thickness",
description="The ratio between inner and outside diameters",
default=.3
)
strandsize : FloatProperty(
name="Bevel Depth",
description="Individual strand diameter (similar to Curve's Bevel depth)",
default=.3,
min=.01, max=10
)
width : FloatProperty(
name="Width",
description="Stretch the Braids along the Z axis",
default=.2
)
resolution : IntProperty(
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
)
pointy : BoolProperty(
name="Pointy",
description="Switch between round and sharp corners",
default=False
)
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.label(text="Settings:")
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")
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:
ob.select_set(False)
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
if self.edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
else:
bpy.ops.object.mode_set(mode = 'OBJECT')
return {'FINISHED'}
def register():
bpy.utils.register_class(Braid)
def unregister():
bpy.utils.unregister_class(Braid)
if __name__ == "__main__":
register()