More unit tests

Copy in a multifile addon for testing, also fix a typo in blenderpack.py
This commit is contained in:
2017-06-22 17:47:38 -07:00
parent 44e0a904a4
commit 20683af72c
13 changed files with 7538 additions and 1 deletions

View File

@@ -22,6 +22,13 @@ class test_blenderpack_make_repo(unittest.TestCase):
reality = str(blenderpack.extract_blinfo(os.path.join(self.helper_path, 'addons', 'add_curve_extra_objects.zip')))
self.assertEqual(expectation, reality)
def test_extract_blinfo_from_dir(self):
with open(os.path.join(self.helper_path, 'extra_objects_blinfo.txt'), 'r') as f:
expectation = f.read()
reality = str(blenderpack.extract_blinfo(os.path.join(self.helper_path, 'addons', 'add_curve_extra_objects/')))
self.assertEqual(expectation, reality)
# def test_validpath(self):
# blenderpack.make_repo(os.path.join('test_helpers', 'addons'))

View File

@@ -0,0 +1,301 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Contributed to by:
# testscreenings, Alejandro Omar Chocano Vasquez, Jimmy Hazevoet, meta-androcto #
# Cmomoney, Jared Forsyth, Adam Newgas, Spivak Vladimir, Jared Forsyth, Atom #
# Antonio Osprite, Marius Giurgi (DolphinDream)
bl_info = {
"name": "Extra Objects",
"author": "Multiple Authors",
"version": (0, 1, 2),
"blender": (2, 76, 0),
"location": "View3D > Add > Curve > Extra Objects",
"description": "Add extra curve object types",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Curve/Curve_Objects",
"category": "Add Curve"
}
if "bpy" in locals():
import importlib
importlib.reload(add_curve_aceous_galore)
importlib.reload(add_curve_spirals)
importlib.reload(add_curve_torus_knots)
importlib.reload(add_surface_plane_cone)
importlib.reload(add_curve_curly)
importlib.reload(beveltaper_curve)
importlib.reload(add_curve_celtic_links)
importlib.reload(add_curve_braid)
importlib.reload(add_curve_simple)
importlib.reload(add_curve_spirofit_bouncespline)
else:
from . import add_curve_aceous_galore
from . import add_curve_spirals
from . import add_curve_torus_knots
from . import add_surface_plane_cone
from . import add_curve_curly
from . import beveltaper_curve
from . import add_curve_celtic_links
from . import add_curve_braid
from . import add_curve_simple
from . import add_curve_spirofit_bouncespline
import bpy
from bpy.types import (
Menu,
AddonPreferences,
)
from bpy.props import (
StringProperty,
BoolProperty,
)
def convert_old_presets(data_path, msg_data_path, old_preset_subdir,
new_preset_subdir, fixdic={}, ext=".py"):
"""
convert old presets
"""
def convert_presets(self, context):
if not getattr(self, data_path, False):
return None
import os
target_path = os.path.join("presets", old_preset_subdir)
target_path = bpy.utils.user_resource('SCRIPTS',
target_path)
# created an anytype op to run against preset
op = type('', (), {})()
files = [f for f in os.listdir(target_path) if f.endswith(ext)]
if not files:
print("No old presets in %s" % target_path)
setattr(self, msg_data_path, "No old presets")
return None
new_target_path = os.path.join("presets", new_preset_subdir)
new_target_path = bpy.utils.user_resource('SCRIPTS',
new_target_path,
create=True)
for f in files:
file = open(os.path.join(target_path, f))
for line in file:
if line.startswith("op."):
exec(line)
file.close()
for key, items in fixdic.items():
if hasattr(op, key) and isinstance(getattr(op, key), int):
setattr(op, key, items[getattr(op, key)])
# create a new one
new_file_path = os.path.join(new_target_path, f)
if os.path.isfile(new_file_path):
# do nothing
print("Preset %s already exists, passing..." % f)
continue
file_preset = open(new_file_path, 'w')
file_preset.write("import bpy\n")
file_preset.write("op = bpy.context.active_operator\n")
for prop, value in vars(op).items():
if isinstance(value, str):
file_preset.write("op.%s = '%s'\n" % (prop, str(value)))
else:
file_preset.write("op.%s = %s\n" % (prop, str(value)))
file_preset.close()
print("Writing new preset to %s" % new_file_path)
setattr(self, msg_data_path, "Converted %d old presets" % len(files))
return None
return convert_presets
# Addons Preferences
class CurveExtraObjectsAddonPreferences(AddonPreferences):
bl_idname = __name__
spiral_fixdic = {
"spiral_type": ['ARCH', 'ARCH', 'LOG', 'SPHERE', 'TORUS'],
"curve_type": ['POLY', 'NURBS'],
"spiral_direction": ['COUNTER_CLOCKWISE', 'CLOCKWISE']
}
update_spiral_presets_msg = StringProperty(
default="Nothing to do"
)
update_spiral_presets = BoolProperty(
name="Update Old Presets",
description="Update presets to reflect data changes",
default=False,
update=convert_old_presets(
"update_spiral_presets", # this props name
"update_spiral_presets_msg", # message prop
"operator/curve.spirals",
"curve_extras/curve.spirals",
fixdic=spiral_fixdic
)
)
show_menu_list = BoolProperty(
name="Menu List",
description="Show/Hide the Add Menu items",
default=False
)
show_panel_list = BoolProperty(
name="Panels List",
description="Show/Hide the Panel items",
default=False
)
def draw(self, context):
layout = self.layout
box = layout.box()
box.label(text="Spirals:")
if self.update_spiral_presets:
box.label(self.update_spiral_presets_msg, icon="FILE_TICK")
else:
box.prop(self, "update_spiral_presets")
icon_1 = "TRIA_RIGHT" if not self.show_menu_list else "TRIA_DOWN"
box = layout.box()
box.prop(self, "show_menu_list", emboss=False, icon=icon_1)
if self.show_menu_list:
box.label(text="Items located in the Add Menu > Curve (default shortcut Ctrl + A):",
icon="LAYER_USED")
box.label(text="2D Objects:", icon="LAYER_ACTIVE")
box.label(text="Angle, Arc, Circle, Distance, Ellipse, Line, Point, Polygon,",
icon="LAYER_USED")
box.label(text="Polygon ab, Rectangle, Rhomb, Sector, Segment, Trapezoid",
icon="LAYER_USED")
box.label(text="Curve Profiles:", icon="LAYER_ACTIVE")
box.label(text="Arc, Arrow, Cogwheel, Cycloid, Flower, Helix (3D),",
icon="LAYER_USED")
box.label(text="Noise (3D), Nsided, Profile, Rectangle, Splat, Star",
icon="LAYER_USED")
box.label(text="Curve Spirals:", icon="LAYER_ACTIVE")
box.label(text="Archemedian, Logarithmic, Spheric, Torus",
icon="LAYER_USED")
box.label(text="Knots:", icon="LAYER_ACTIVE")
box.label(text="Torus Knots Plus, Celtic Links, Braid Knot",
icon="LAYER_USED")
box.label(text="Curly Curve", icon="LAYER_ACTIVE")
box.label(text="Bevel/Taper:", icon="LAYER_ACTIVE")
box.label(text="Add Curve as Bevel, Add Curve as Taper",
icon="LAYER_USED")
box.label(text="Items located in the Add Menu > Surface (default shortcut Ctrl + A):",
icon="LAYER_USED")
box.label(text="Wedge, Cone, Star, Plane",
icon="LAYER_ACTIVE")
icon_2 = "TRIA_RIGHT" if not self.show_panel_list else "TRIA_DOWN"
box = layout.box()
box.prop(self, "show_panel_list", emboss=False, icon=icon_2)
if self.show_panel_list:
box.label(text="Panel located in 3D View Tools Region > Create:",
icon="LAYER_ACTIVE")
box.label(text="Spline:", icon="LAYER_ACTIVE")
box.label(text="SpiroFit, Bounce Spline, Catenary", icon="LAYER_USED")
box.label(text="Panel located in 3D View Tools Region > Tools:",
icon="LAYER_ACTIVE")
box.label(text="Simple Curve:", icon="LAYER_ACTIVE")
box.label(text="Available if the Active Object is a Curve was created with 2D Objects",
icon="LAYER_USED")
class INFO_MT_curve_knots_add1(Menu):
# Define the "Extras" menu
bl_idname = "curve_knots_add"
bl_label = "Plants"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("curve.torus_knot_plus", text="Torus Knot Plus")
layout.operator("curve.celtic_links", text="Celtic Links")
layout.operator("mesh.add_braid", text="Braid Knot")
# Define "Extras" menus
def menu_func(self, context):
if context.mode != 'OBJECT':
# fix in D2142 will allow to work in EDIT_CURVE
return None
layout = self.layout
layout.operator_menu_enum("mesh.curveaceous_galore", "ProfileType",
icon='CURVE_DATA')
layout.operator_menu_enum("curve.spirals", "spiral_type",
icon='CURVE_DATA')
layout.separator()
layout.menu("curve_knots_add", text="Knots", icon='CURVE_DATA')
layout.separator()
layout.operator("curve.curlycurve", text="Curly Curve",
icon='CURVE_DATA')
layout.menu("OBJECT_MT_bevel_taper_curve_menu", text="Bevel/Taper",
icon='CURVE_DATA')
def menu_surface(self, context):
self.layout.separator()
if context.mode == 'EDIT_SURFACE':
self.layout.operator("curve.smooth_x_times",
text="Special Smooth", icon="MOD_CURVE")
elif context.mode == 'OBJECT':
self.layout.operator("object.add_surface_wedge", text="Wedge",
icon="SURFACE_DATA")
self.layout.operator("object.add_surface_cone", text="Cone",
icon="SURFACE_DATA")
self.layout.operator("object.add_surface_star", text="Star",
icon="SURFACE_DATA")
self.layout.operator("object.add_surface_plane", text="Plane",
icon="SURFACE_DATA")
def register():
add_curve_simple.register()
bpy.utils.register_module(__name__)
# Add "Extras" menu to the "Add Curve" menu
bpy.types.INFO_MT_curve_add.append(menu_func)
# Add "Extras" menu to the "Add Surface" menu
bpy.types.INFO_MT_surface_add.append(menu_surface)
def unregister():
add_curve_simple.unregister()
# Remove "Extras" menu from the "Add Curve" menu.
bpy.types.INFO_MT_curve_add.remove(menu_func)
# Remove "Extras" menu from the "Add Surface" menu.
bpy.types.INFO_MT_surface_add.remove(menu_surface)
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,261 @@
# gpl: author Jared Forsyth <github.com/jaredly>
"""
bl_info = {
"name": "New Braid",
"author": "Jared Forsyth <github.com/jaredly>",
"version": (1, 0, 2),
"blender": (2, 6, 0),
"location": "View3D > Add > Mesh > New Braid",
"description": "Adds a new Braid",
"warning": "",
"wiki_url": "",
"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'
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 = True
return circle
def defaultStar():
star = poly_lines('star', 'staz', [tuple(star_pts(points=5, r=.5, ir=.05))], type='NURBS')
star.hide = 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 = "mesh.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
)
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column(align=True)
col.label("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("Geometry Options:")
col.prop(self, "strandsize")
col.prop(self, "resolution")
def execute(self, context):
circle = defaultCircle(self.strandsize)
context.scene.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.objects.link(braid)
for ob in context.scene.objects:
ob.select = False
base.select = True
context.scene.objects.active = braid
return {'FINISHED'}
def register():
bpy.utils.register_class(Braid)
def unregister():
bpy.utils.unregister_class(Braid)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,284 @@
# Blender plugin for generating celtic knot curves from 3d meshes
#
# The MIT License (MIT)
#
# Copyright (c) 2013 Adam Newgas
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
bl_info = {
"name": "Celtic Knot",
"description": "",
"author": "Adam Newgas",
"version": (0, 1, 2),
"blender": (2, 74, 0),
"location": "View3D > Add > Curve",
"warning": "",
"wiki_url": "https://github.com/BorisTheBrave/celtic-knot/wiki",
"category": "Add Curve"}
import bpy
import bmesh
from bpy.types import Operator
from bpy.props import (
EnumProperty,
FloatProperty,
)
from collections import defaultdict
from math import (
pi, sin,
cos,
)
class CelticKnotOperator(Operator):
bl_idname = "curve.celtic_links"
bl_label = "Celtic Links"
bl_description = "Select a low poly Mesh Object to cover with Knitted Links"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
weave_up = FloatProperty(
name="Weave Up",
description="Distance to shift curve upwards over knots",
subtype="DISTANCE",
unit="LENGTH"
)
weave_down = FloatProperty(
name="Weave Down",
description="Distance to shift curve downward under knots",
subtype="DISTANCE",
unit="LENGTH"
)
handle_types = [
('ALIGNED', "Aligned", "Points at a fixed crossing angle"),
('AUTO', "Auto", "Automatic control points")
]
handle_type = EnumProperty(
items=handle_types,
name="Handle Type",
description="Controls what type the bezier control points use",
default='AUTO'
)
handle_type_map = {"AUTO": "AUTOMATIC", "ALIGNED": "ALIGNED"}
crossing_angle = FloatProperty(
name="Crossing Angle",
description="Aligned only: the angle between curves in a knot",
default=pi / 4,
min=0, max=pi / 2,
subtype="ANGLE",
unit="ROTATION"
)
crossing_strength = FloatProperty(
name="Crossing Strength",
description="Aligned only: strenth of bezier control points",
soft_min=0,
subtype="DISTANCE",
unit="LENGTH"
)
geo_bDepth = FloatProperty(
name="Bevel Depth",
default=0.04,
min=0, soft_min=0,
description="Bevel Depth",
)
@classmethod
def poll(cls, context):
ob = context.active_object
return ((ob is not None) and (ob.mode == "OBJECT") and
(ob.type == "MESH") and (context.mode == "OBJECT"))
def draw(self, context):
layout = self.layout
layout.prop(self, "handle_type")
col = layout.column(align=True)
col.prop(self, "weave_up")
col.prop(self, "weave_down")
col = layout.column(align=True)
col.active = False if self.handle_type == 'AUTO' else True
col.prop(self, "crossing_angle")
col.prop(self, "crossing_strength")
layout.prop(self, "geo_bDepth")
def execute(self, context):
# Cache some values
s = sin(self.crossing_angle) * self.crossing_strength
c = cos(self.crossing_angle) * self.crossing_strength
handle_type = self.handle_type
weave_up = self.weave_up
weave_down = self.weave_down
# Create the new object
orig_obj = obj = context.active_object
curve = bpy.data.curves.new("Celtic", "CURVE")
curve.dimensions = "3D"
curve.twist_mode = "MINIMUM"
curve.fill_mode = "FULL"
curve.bevel_depth = 0.015
curve.extrude = 0.003
curve.bevel_resolution = 4
obj = obj.data
midpoints = []
# Compute all the midpoints of each edge
for e in obj.edges.values():
v1 = obj.vertices[e.vertices[0]]
v2 = obj.vertices[e.vertices[1]]
m = (v1.co + v2.co) / 2.0
midpoints.append(m)
bm = bmesh.new()
bm.from_mesh(obj)
# Stores which loops the curve has already passed through
loops_entered = defaultdict(lambda: False)
loops_exited = defaultdict(lambda: False)
# Loops on the boundary of a surface
def ignorable_loop(loop):
return len(loop.link_loops) == 0
# Starting at loop, build a curve one vertex at a time
# until we start where we came from
# Forward means that for any two edges the loop crosses
# sharing a face, it is passing through in clockwise order
# else anticlockwise
def make_loop(loop, forward):
current_spline = curve.splines.new("BEZIER")
current_spline.use_cyclic_u = True
first = True
# Data for the spline
# It's faster to store in an array and load into blender
# at once
cos = []
handle_lefts = []
handle_rights = []
while True:
if forward:
if loops_exited[loop]:
break
loops_exited[loop] = True
# Follow the face around, ignoring boundary edges
while True:
loop = loop.link_loop_next
if not ignorable_loop(loop):
break
assert loops_entered[loop] is False
loops_entered[loop] = True
v = loop.vert.index
prev_loop = loop
# Find next radial loop
assert loop.link_loops[0] != loop
loop = loop.link_loops[0]
forward = loop.vert.index == v
else:
if loops_entered[loop]:
break
loops_entered[loop] = True
# Follow the face around, ignoring boundary edges
while True:
v = loop.vert.index
loop = loop.link_loop_prev
if not ignorable_loop(loop):
break
assert loops_exited[loop] is False
loops_exited[loop] = True
prev_loop = loop
# Find next radial loop
assert loop.link_loops[-1] != loop
loop = loop.link_loops[-1]
forward = loop.vert.index == v
if not first:
current_spline.bezier_points.add()
first = False
midpoint = midpoints[loop.edge.index]
normal = loop.calc_normal() + prev_loop.calc_normal()
normal.normalize()
offset = weave_up if forward else weave_down
midpoint = midpoint + offset * normal
cos.extend(midpoint)
if handle_type != "AUTO":
tangent = loop.link_loop_next.vert.co - loop.vert.co
tangent.normalize()
binormal = normal.cross(tangent).normalized()
if not forward:
tangent *= -1
s_binormal = s * binormal
c_tangent = c * tangent
handle_left = midpoint - s_binormal - c_tangent
handle_right = midpoint + s_binormal + c_tangent
handle_lefts.extend(handle_left)
handle_rights.extend(handle_right)
points = current_spline.bezier_points
points.foreach_set("co", cos)
if handle_type != "AUTO":
points.foreach_set("handle_left", handle_lefts)
points.foreach_set("handle_right", handle_rights)
# Attempt to start a loop at each untouched loop in the entire mesh
for face in bm.faces:
for loop in face.loops:
if ignorable_loop(loop):
continue
if not loops_exited[loop]:
make_loop(loop, True)
if not loops_entered[loop]:
make_loop(loop, False)
# Create an object from the curve
from bpy_extras import object_utils
object_utils.object_data_add(context, curve, operator=None)
# Set the handle type (this is faster than setting it pointwise)
bpy.ops.object.editmode_toggle()
bpy.ops.curve.select_all(action="SELECT")
bpy.ops.curve.handle_type_set(type=self.handle_type_map[handle_type])
# Some blender versions lack the default
bpy.ops.curve.radius_set(radius=1.0)
bpy.ops.object.editmode_toggle()
# Restore active selection
curve_obj = context.active_object
# apply the bevel setting since it was unused
try:
curve_obj.data.bevel_depth = self.geo_bDepth
except:
pass
context.scene.objects.active = orig_obj
return {'FINISHED'}
def register():
bpy.utils.register_class(CelticKnotOperator)
def unregister():
bpy.utils.unregister_class(CelticKnotOperator)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,491 @@
# gpl: author Cmomoney
# DevBo Task https://developer.blender.org/T37299
bl_info = {
"name": "Curly Curves",
"author": "Cmomoney",
"version": (1, 1, 8),
"blender": (2, 69, 0),
"location": "View3D > Add > Curve > Curly Curve",
"description": "Adds a new Curly Curve",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
"Py/Scripts/Curve/Curly_Curves",
"category": "Add Curve"}
import bpy
from bpy.types import Operator
from bpy.props import (
FloatProperty,
IntProperty,
)
from bpy_extras.object_utils import (
AddObjectHelper,
object_data_add,
)
def add_type6(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[0.047131 * scale_x, 0.065832 * scale_y,
0.0, 0.010396 * scale_x, -0.186771 * scale_y,
0.0, 0.076107 * scale_x, 0.19414 * scale_y,
0.0, 0.0 * scale_x, -1.0 * scale_y, 0.0],
[0.451396 * scale_x, -0.48376 * scale_y,
0.0, 0.433623 * scale_x, -0.587557 * scale_y,
0.0, 0.525837 * scale_x, -0.423363 * scale_y,
0.0, 0.15115 * scale_x, -0.704345 * scale_y, 0.0]
]
lhandles = [
[(-0.067558 * scale_x, 0.078418 * scale_y, 0.0),
(0.168759 * scale_x, -0.154334 * scale_y, 0.0),
(-0.236823 * scale_x, 0.262436 * scale_y, 0.0),
(0.233116 * scale_x, -0.596115 * scale_y, 0.0)],
[(0.498001 * scale_x, -0.493434 * scale_y, 0.0),
(0.375618 * scale_x, -0.55465 * scale_y, 0.0),
(0.634373 * scale_x, -0.49873 * scale_y, 0.0),
(0.225277 * scale_x, -0.526814 * scale_y, 0.0)]
]
rhandles = [
[(0.161825 * scale_x, 0.053245 * scale_y, 0.0),
(-0.262003 * scale_x, -0.242566 * scale_y, 0.0),
(0.519691 * scale_x, 0.097329 * scale_y, 0.0),
(-0.233116 * scale_x, -1.403885 * scale_y, 0.0)],
[(0.404788 * scale_x, -0.474085 * scale_y, 0.0),
(0.533397 * scale_x, -0.644158 * scale_y, 0.0),
(0.371983 * scale_x, -0.316529 * scale_y, 0.0),
(0.077022 * scale_x, -0.881876 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type5(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[0.047131 * scale_x, 0.065832 * scale_y, 0.0,
0.010396 * scale_x, -0.186771 * scale_y, 0.0,
0.076107 * scale_x, 0.19414 * scale_y, 0.0,
0.0 * scale_x, -1.0 * scale_y, 0.0],
[0.086336 * scale_x, -0.377611 * scale_y, 0.0,
0.022417 * scale_x, -0.461301 * scale_y, 0.0,
0.079885 * scale_x, -0.281968 * scale_y, 0.0,
0.129212 * scale_x, -0.747702 * scale_y, 0.0]
]
lhandles = [
[(-0.067558 * scale_x, 0.078419 * scale_y, 0.0),
(0.168759 * scale_x, -0.154335 * scale_y, 0.0),
(-0.236823 * scale_x, 0.262436 * scale_y, 0.0),
(0.233116 * scale_x, -0.596115 * scale_y, 0.0)],
[(0.047518 * scale_x, -0.350065 * scale_y, 0.0),
(0.086012 * scale_x, -0.481379 * scale_y, 0.0),
(-0.049213 * scale_x, -0.253793 * scale_y, 0.0),
(0.208763 * scale_x, -0.572534 * scale_y, 0.0)]
]
rhandles = [
[(0.161825 * scale_x, 0.053245 * scale_y, 0.0),
(-0.262003 * scale_x, -0.242566 * scale_y, 0.0),
(0.519691 * scale_x, 0.097329 * scale_y, 0.0),
(-0.233116 * scale_x, -1.403885 * scale_y, 0.0)],
[(0.125156 * scale_x, -0.405159 * scale_y, 0.0),
(-0.086972 * scale_x, -0.426766 * scale_y, 0.0),
(0.262886 * scale_x, -0.321908 * scale_y, 0.0),
(0.049661 * scale_x, -0.92287 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type8(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.850431 * scale_x, -0.009091 * scale_y,
0.0, -0.818807 * scale_x, -0.130518 * scale_y,
0.0, -0.944931 * scale_x, 0.055065 * scale_y,
0.0, -0.393355 * scale_x, -0.035521 * scale_y,
0.0, 0.0 * scale_x, 0.348298 * scale_y,
0.0, 0.393355 * scale_x, -0.035521 * scale_y,
0.0, 0.978373 * scale_x, 0.185638 * scale_y,
0.0, 0.771617 * scale_x, 0.272819 * scale_y,
0.0, 0.864179 * scale_x, 0.188103 * scale_y, 0.0]
]
lhandles = [
[(-0.90478 * scale_x, -0.025302 * scale_y, 0.0),
(-0.753279 * scale_x, -0.085571 * scale_y, 0.0),
(-1.06406 * scale_x, -0.047879 * scale_y, 0.0),
(-0.622217 * scale_x, -0.022501 * scale_y, 0.0),
(0.181 * scale_x, 0.34879 * scale_y, 0.0),
(-0.101464 * scale_x, -0.063669 * scale_y, 0.0),
(0.933064 * scale_x, 0.03001 * scale_y, 0.0),
(0.82418 * scale_x, 0.39899 * scale_y, 0.0),
(0.827377 * scale_x, 0.144945 * scale_y, 0.0)]
]
rhandles = [
[(-0.796079 * scale_x, 0.007121 * scale_y, 0.0),
(-0.931521 * scale_x, -0.207832 * scale_y, 0.0),
(-0.822288 * scale_x, 0.161045 * scale_y, 0.0),
(0.101464 * scale_x, -0.063671 * scale_y, 0.0),
(-0.181193 * scale_x, 0.347805 * scale_y, 0.0),
(0.622217 * scale_x, -0.022502 * scale_y, 0.0),
(1.022383 * scale_x, 0.336808 * scale_y, 0.0),
(0.741059 * scale_x, 0.199468 * scale_y, 0.0),
(0.900979 * scale_x, 0.231258 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type3(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.78652 * scale_x, -0.070157 * scale_y,
0.0, -0.697972 * scale_x, -0.247246 * scale_y,
0.0, -0.953385 * scale_x, -0.002048 * scale_y,
0.0, 0.0 * scale_x, 0.0 * scale_y,
0.0, 0.917448 * scale_x, 0.065788 * scale_y,
0.0, 0.448535 * scale_x, 0.515947 * scale_y,
0.0, 0.6111 * scale_x, 0.190831 * scale_y, 0.0]
]
lhandles = [
[(-0.86511 * scale_x, -0.112965 * scale_y, 0.0),
(-0.61153 * scale_x, -0.156423 * scale_y, 0.0),
(-1.103589 * scale_x, -0.199934 * scale_y, 0.0),
(-0.446315 * scale_x, 0.135163 * scale_y, 0.0),
(0.669383 * scale_x, -0.254463 * scale_y, 0.0),
(0.721512 * scale_x, 0.802759 * scale_y, 0.0),
(0.466815 * scale_x, 0.112232 * scale_y, 0.0)]
]
rhandles = [
[(-0.707927 * scale_x, -0.027348 * scale_y, 0.0),
(-0.846662 * scale_x, -0.40347 * scale_y, 0.0),
(-0.79875 * scale_x, 0.201677 * scale_y, 0.0),
(0.446315 * scale_x, -0.135163 * scale_y, 0.0),
(1.196752 * scale_x, 0.42637 * scale_y, 0.0),
(0.289834 * scale_x, 0.349204 * scale_y, 0.0),
(0.755381 * scale_x, 0.269428 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type2(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.719632 * scale_x, -0.08781 * scale_y,
0.0, -0.605138 * scale_x, -0.31612 * scale_y,
0.0, -0.935392 * scale_x, 0.0, 0.0, 0.0, 0.0,
0.0, 0.935392 * scale_x, 0.0, 0.0, 0.605138 * scale_x,
-0.316119 * scale_y, 0.0, 0.719632 * scale_x, -0.08781 * scale_y, 0.0]
]
lhandles = [
[(-0.82125 * scale_x, -0.142999 * scale_y, 0.0),
(-0.493366 * scale_x, -0.199027 * scale_y, 0.0),
(-1.129601 * scale_x, -0.25513 * scale_y, 0.0),
(-0.467584 * scale_x, 0.00044 * scale_y, 0.0),
(0.735439 * scale_x, 0.262646 * scale_y, 0.0),
(0.797395 * scale_x, -0.517531 * scale_y, 0.0),
(0.618012 * scale_x, -0.032614 * scale_y, 0.0)]
]
rhandles = [
[(-0.618009 * scale_x, -0.032618 * scale_y, 0.0),
(-0.797396 * scale_x, -0.517532 * scale_y, 0.0),
(-0.735445 * scale_x, 0.262669 * scale_y, 0.0),
(0.468041 * scale_x, -0.00044 * scale_y, 0.0),
(1.129616 * scale_x, -0.255119 * scale_y, 0.0),
(0.493365 * scale_x, -0.199025 * scale_y, 0.0),
(0.821249 * scale_x, -0.143004 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type10(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.999637 * scale_x, 0.000348 * scale_y,
0.0, 0.259532 * scale_x, -0.017841 * scale_y,
0.0, 0.482303 * scale_x, 0.780429 * scale_y,
0.0, 0.573183 * scale_x, 0.506898 * scale_y, 0.0],
[0.259532 * scale_x, -0.017841 * scale_y,
0.0, 0.554919 * scale_x, -0.140918 * scale_y,
0.0, 0.752264 * scale_x, -0.819275 * scale_y,
0.0, 0.824152 * scale_x, -0.514881 * scale_y, 0.0]
]
lhandles = [
[(-1.258333 * scale_x, -0.258348 * scale_y, 0.0),
(-0.240006 * scale_x, -0.15259 * scale_y, 0.0),
(0.79037 * scale_x, 0.857575 * scale_y, 0.0),
(0.376782 * scale_x, 0.430157 * scale_y, 0.0)],
[(0.224917 * scale_x, -0.010936 * scale_y, 0.0),
(0.514858 * scale_x, -0.122809 * scale_y, 0.0),
(1.057957 * scale_x, -0.886925 * scale_y, 0.0),
(0.61945 * scale_x, -0.464285 * scale_y, 0.0)]
]
rhandles = [
[(-0.74094 * scale_x, 0.259045 * scale_y, 0.0),
(0.768844 * scale_x, 0.119545 * scale_y, 0.0),
(0.279083 * scale_x, 0.729538 * scale_y, 0.0),
(0.643716 * scale_x, 0.534458 * scale_y, 0.0)],
[(0.294147 * scale_x, -0.024746 * scale_y, 0.0),
(1.03646 * scale_x, -0.358598 * scale_y, 0.0),
(0.547718 * scale_x, -0.774008 * scale_y, 0.0),
(0.897665 * scale_x, -0.533051 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type9(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[0.260968 * scale_x, -0.668118 * scale_y,
0.0, 0.108848 * scale_x, -0.381587 * scale_y,
0.0, 0.537002 * scale_x, -0.77303 * scale_y,
0.0, -0.600421 * scale_x, -0.583106 * scale_y,
0.0, -0.600412 * scale_x, 0.583103 * scale_y,
0.0, 0.537002 * scale_x, 0.773025 * scale_y,
0.0, 0.108854 * scale_x, 0.381603 * scale_y,
0.0, 0.260966 * scale_x, 0.668129 * scale_y, 0.0]
]
lhandles = [
[(0.387973 * scale_x, -0.594856 * scale_y, 0.0),
(-0.027835 * scale_x, -0.532386 * scale_y, 0.0),
(0.775133 * scale_x, -0.442883 * scale_y, 0.0),
(-0.291333 * scale_x, -1.064385 * scale_y, 0.0),
(-0.833382 * scale_x, 0.220321 * scale_y, 0.0),
(0.291856 * scale_x, 1.112891 * scale_y, 0.0),
(0.346161 * scale_x, 0.119777 * scale_y, 0.0),
(0.133943 * scale_x, 0.741389 * scale_y, 0.0)]
]
rhandles = [
[(0.133951 * scale_x, -0.741386 * scale_y, 0.0),
(0.346154 * scale_x, -0.119772 * scale_y, 0.0),
(0.291863 * scale_x, -1.112896 * scale_y, 0.0),
(-0.833407 * scale_x, -0.220324 * scale_y, 0.0),
(-0.29134 * scale_x, 1.064389 * scale_y, 0.0),
(0.775125 * scale_x, 0.442895 * scale_y, 0.0),
(-0.029107 * scale_x, 0.533819 * scale_y, 0.0),
(0.387981 * scale_x, 0.594873 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type7(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.850431 * scale_x, -0.009091 * scale_y,
0.0, -0.818807 * scale_x, -0.130518 * scale_y,
0.0, -0.944931 * scale_x, 0.055065 * scale_y, 0.0,
-0.393355 * scale_x, -0.035521 * scale_y,
0.0, 0.0 * scale_x, 0.348298 * scale_y,
0.0, 0.393355 * scale_x, -0.035521 * scale_y,
0.0, 0.944931 * scale_x, 0.055065 * scale_y,
0.0, 0.818807 * scale_x, -0.130518 * scale_y,
0.0, 0.850431 * scale_x, -0.009091 * scale_y, 0.0]
]
lhandles = [
[(-0.90478 * scale_x, -0.025302 * scale_y, 0.0),
(-0.753279 * scale_x, -0.085571 * scale_y, 0.0),
(-1.06406 * scale_x, -0.047879 * scale_y, 0.0),
(-0.622217 * scale_x, -0.022502 * scale_y, 0.0),
(0.181 * scale_x, 0.348791 * scale_y, 0.0),
(-0.101464 * scale_x, -0.063671 * scale_y, 0.0),
(0.822288 * scale_x, 0.161045 * scale_y, 0.0),
(0.931521 * scale_x, -0.207832 * scale_y, 0.0),
(0.796079 * scale_x, 0.007121 * scale_y, 0.0)]
]
rhandles = [
[(-0.796079 * scale_x, 0.007121 * scale_y, 0.0),
(-0.931521 * scale_x, -0.207832 * scale_y, 0.0),
(-0.822288 * scale_x, 0.161045 * scale_y, 0.0),
(0.101464 * scale_x, -0.063671 * scale_y, 0.0),
(-0.181193 * scale_x, 0.347805 * scale_y, 0.0),
(0.622217 * scale_x, -0.022502 * scale_y, 0.0),
(1.06406 * scale_x, -0.047879 * scale_y, 0.0),
(0.753279 * scale_x, -0.085571 * scale_y, 0.0),
(0.90478 * scale_x, -0.025302 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type4(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[0.072838 * scale_x, -0.071461 * scale_y,
0.0, -0.175451 * scale_x, -0.130711 * scale_y,
0.0, 0.207269 * scale_x, 0.118064 * scale_y,
0.0, 0 * scale_x, -1.0 * scale_y, 0.0]
]
lhandles = [
[(0.042135 * scale_x, 0.039756 * scale_y, 0),
(-0.086769 * scale_x, -0.265864 * scale_y, 0),
(0.002865 * scale_x, 0.364657 * scale_y, 0),
(0.233116 * scale_x, -0.596115 * scale_y, 0)]
]
rhandles = [
[(0.103542 * scale_x, -0.182683 * scale_y, 0),
(-0.327993 * scale_x, 0.101765 * scale_y, 0),
(0.417702 * scale_x, -0.135803 * scale_y, 0),
(-0.233116 * scale_x, -1.403885 * scale_y, 0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type1(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.71753 * scale_x, -0.08781 * scale_y,
0, -0.60337 * scale_x, -0.31612 * scale_y, 0,
-0.93266 * scale_x, 0, 0, 0, 0, 0, 0.93266 * scale_x,
0, 0, 0.60337 * scale_x, 0.31612 * scale_y,
0, 0.71753 * scale_x, 0.08781 * scale_y, 0]
]
lhandles = [
[(-0.81885 * scale_x, -0.143002 * scale_y, 0),
(-0.491926 * scale_x, -0.199026 * scale_y, 0),
(-1.126316 * scale_x, -0.255119 * scale_y, 0),
(-0.446315 * scale_x, 0.135164 * scale_y, 0),
(0.733297 * scale_x, -0.26265 * scale_y, 0),
(0.795065 * scale_x, 0.517532 * scale_y, 0),
(0.616204 * scale_x, 0.03262 * scale_y, 0)]
]
rhandles = [
[(-0.616204 * scale_x, -0.032618 * scale_y, 0),
(-0.795067 * scale_x, -0.517532 * scale_y, 0),
(-0.733297 * scale_x, 0.262651 * scale_y, 0),
(0.446315 * scale_x, -0.135163 * scale_y, 0),
(1.126316 * scale_x, 0.255119 * scale_y, 0),
(0.491924 * scale_x, 0.199026 * scale_y, 0),
(0.81885 * scale_x, 0.143004 * scale_y, 0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def make_curve(self, context, verts, lh, rh):
types = self.types
curve_data = bpy.data.curves.new(name='CurlyCurve', type='CURVE')
curve_data.dimensions = '3D'
for p in range(len(verts)):
c = 0
spline = curve_data.splines.new(type='BEZIER')
spline.bezier_points.add(len(verts[p]) / 3 - 1)
spline.bezier_points.foreach_set('co', verts[p])
for bp in spline.bezier_points:
bp.handle_left_type = 'ALIGNED'
bp.handle_right_type = 'ALIGNED'
bp.handle_left.xyz = lh[p][c]
bp.handle_right.xyz = rh[p][c]
c += 1
# something weird with this one
if types == 1 or types == 2 or types == 3:
spline.bezier_points[3].handle_left.xyz = lh[p][3]
object_data_add(context, curve_data, operator=self)
class add_curlycurve(Operator, AddObjectHelper):
bl_idname = "curve.curlycurve"
bl_label = "Add Curly Curve"
bl_description = "Create a Curly Curve"
bl_options = {'REGISTER', 'UNDO'}
types = IntProperty(
name="Type",
description="Type of curly curve",
default=1,
min=1, max=10
)
scale_x = FloatProperty(
name="Scale X",
description="Scale on X axis",
default=1.0
)
scale_y = FloatProperty(
name="Scale Y",
description="Scale on Y axis",
default=1.0
)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
# AddObjectHelper props
col.prop(self, "view_align")
col.prop(self, "location")
col.prop(self, "rotation")
col = layout.column()
col.label("Curve:")
col.prop(self, "types")
col = layout.column(align=True)
col.label("Resize:")
col.prop(self, "scale_x")
col.prop(self, "scale_y")
def execute(self, context):
if self.types == 1:
add_type1(self, context)
if self.types == 2:
add_type2(self, context)
if self.types == 3:
add_type3(self, context)
if self.types == 4:
add_type4(self, context)
if self.types == 5:
add_type5(self, context)
if self.types == 6:
add_type6(self, context)
if self.types == 7:
add_type7(self, context)
if self.types == 8:
add_type8(self, context)
if self.types == 9:
add_type9(self, context)
if self.types == 10:
add_type10(self, context)
return {'FINISHED'}
# Registration
def add_curlycurve_button(self, context):
self.layout.operator(
add_curlycurve.bl_idname,
text="Add Curly Curve",
icon='PLUGIN'
)
def register():
bpy.utils.register_class(add_curlycurve)
bpy.types.INFO_MT_curve_add.append(add_curlycurve_button)
def unregister():
bpy.utils.unregister_class(add_curlycurve)
bpy.types.INFO_MT_curve_add.remove(add_curlycurve_button)
if __name__ == "__main__":
register()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,424 @@
# gpl: author Alejandro Omar Chocano Vasquez
"""
bl_info = {
"name": "Spirals",
"description": "Make spirals",
"author": "Alejandro Omar Chocano Vasquez",
"version": (1, 2, 1),
"blender": (2, 62, 0),
"location": "View3D > Add > Curve",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.4/Py/"
"Scripts/Object/Spirals",
"tracker_url": "http://alexvaqp.googlepages.com?"
"func=detail&aid=<number>",
"category": "Add Curve",
}
"""
import bpy
import time
from bpy.props import (
EnumProperty,
BoolProperty,
FloatProperty,
IntProperty,
)
from math import (
sin, cos, pi
)
from bpy_extras.object_utils import object_data_add
from bpy.types import (
Operator,
Menu,
)
from bl_operators.presets import AddPresetBase
# make normal spiral
# ----------------------------------------------------------------------------
def make_spiral(props, context):
# archemedian and logarithmic can be plotted in cylindrical coordinates
# INPUT: turns->degree->max_phi, steps, direction
# Initialise Polar Coordinate Enviroment
props.degree = 360 * props.turns # If you want to make the slider for degree
steps = props.steps * props.turns # props.steps[per turn] -> steps[for the whole spiral]
props.z_scale = props.dif_z * props.turns
max_phi = pi * props.degree / 180 # max angle in radian
step_phi = max_phi / steps # angle in radians between two vertices
if props.spiral_direction == 'CLOCKWISE':
step_phi *= -1 # flip direction
max_phi *= -1
step_z = props.z_scale / (steps - 1) # z increase in one step
verts = []
verts.extend([props.radius, 0, 0, 1])
cur_phi = 0
cur_z = 0
# Archemedean: dif_radius, radius
cur_rad = props.radius
step_rad = props.dif_radius / (steps * 360 / props.degree)
# radius increase per angle for archemedean spiral|
# (steps * 360/props.degree)...Steps needed for 360 deg
# Logarithmic: radius, B_force, ang_div, dif_z
while abs(cur_phi) <= abs(max_phi):
cur_phi += step_phi
cur_z += step_z
if props.spiral_type == 'ARCH':
cur_rad += step_rad
if props.spiral_type == 'LOG':
# r = a*e^{|theta| * b}
cur_rad = props.radius * pow(props.B_force, abs(cur_phi))
px = cur_rad * cos(cur_phi)
py = cur_rad * sin(cur_phi)
verts.extend([px, py, cur_z, 1])
return verts
# make Spheric spiral
# ----------------------------------------------------------------------------
def make_spiral_spheric(props, context):
# INPUT: turns, steps[per turn], radius
# use spherical Coordinates
step_phi = (2 * pi) / props.steps # Step of angle in radians for one turn
steps = props.steps * props.turns # props.steps[per turn] -> steps[for the whole spiral]
max_phi = 2 * pi * props.turns # max angle in radian
step_phi = max_phi / steps # angle in radians between two vertices
if props.spiral_direction == 'CLOCKWISE': # flip direction
step_phi *= -1
max_phi *= -1
step_theta = pi / (steps - 1) # theta increase in one step (pi == 180 deg)
verts = []
verts.extend([0, 0, -props.radius, 1]) # First vertex at south pole
cur_phi = 0
cur_theta = -pi / 2 # Beginning at south pole
while abs(cur_phi) <= abs(max_phi):
# Coordinate Transformation sphere->rect
px = props.radius * cos(cur_theta) * cos(cur_phi)
py = props.radius * cos(cur_theta) * sin(cur_phi)
pz = props.radius * sin(cur_theta)
verts.extend([px, py, pz, 1])
cur_theta += step_theta
cur_phi += step_phi
return verts
# make torus spiral
# ----------------------------------------------------------------------------
def make_spiral_torus(props, context):
# INPUT: turns, steps, inner_radius, curves_number,
# mul_height, dif_inner_radius, cycles
max_phi = 2 * pi * props.turns * props.cycles # max angle in radian
step_phi = 2 * pi / props.steps # Step of angle in radians between two vertices
if props.spiral_direction == 'CLOCKWISE': # flip direction
step_phi *= -1
max_phi *= -1
step_theta = (2 * pi / props.turns) / props.steps
step_rad = props.dif_radius / (props.steps * props.turns)
step_inner_rad = props.dif_inner_radius / props.steps
step_z = props.dif_z / (props.steps * props.turns)
verts = []
cur_phi = 0 # Inner Ring Radius Angle
cur_theta = 0 # Ring Radius Angle
cur_rad = props.radius
cur_inner_rad = props.inner_radius
cur_z = 0
n_cycle = 0
while abs(cur_phi) <= abs(max_phi):
# Torus Coordinates -> Rect
px = (cur_rad + cur_inner_rad * cos(cur_phi)) * \
cos(props.curves_number * cur_theta)
py = (cur_rad + cur_inner_rad * cos(cur_phi)) * \
sin(props.curves_number * cur_theta)
pz = cur_inner_rad * sin(cur_phi) + cur_z
verts.extend([px, py, pz, 1])
if props.touch and cur_phi >= n_cycle * 2 * pi:
step_z = ((n_cycle + 1) * props.dif_inner_radius +
props.inner_radius) * 2 / (props.steps * props.turns)
n_cycle += 1
cur_theta += step_theta
cur_phi += step_phi
cur_rad += step_rad
cur_inner_rad += step_inner_rad
cur_z += step_z
return verts
def draw_curve(props, context):
if props.spiral_type == 'ARCH':
verts = make_spiral(props, context)
if props.spiral_type == 'LOG':
verts = make_spiral(props, context)
if props.spiral_type == 'SPHERE':
verts = make_spiral_spheric(props, context)
if props.spiral_type == 'TORUS':
verts = make_spiral_torus(props, context)
curve_data = bpy.data.curves.new(name='Spiral', type='CURVE')
curve_data.dimensions = '3D'
spline = curve_data.splines.new(type=props.curve_type)
"""
if props.curve_type == 0:
spline = curve_data.splines.new(type='POLY')
elif props.curve_type == 1:
spline = curve_data.splines.new(type='NURBS')
"""
spline.points.add(len(verts) * 0.25 - 1)
# Add only one quarter of points as elements in verts,
# because verts looks like: "x,y,z,?,x,y,z,?,x,..."
spline.points.foreach_set('co', verts)
new_obj = object_data_add(context, curve_data)
class CURVE_OT_spirals(Operator):
bl_idname = "curve.spirals"
bl_label = "Curve Spirals"
bl_description = "Create different types of spirals"
bl_options = {'REGISTER', 'UNDO'}
spiral_type = EnumProperty(
items=[('ARCH', "Archemedian", "Archemedian"),
("LOG", "Logarithmic", "Logarithmic"),
("SPHERE", "Spheric", "Spheric"),
("TORUS", "Torus", "Torus")],
default='ARCH',
name="Spiral Type",
description="Type of spiral to add"
)
curve_type = EnumProperty(
items=[('POLY', "Poly", "PolyLine"),
("NURBS", "NURBS", "NURBS")],
default='POLY',
name="Curve Type",
description="Type of spline to use"
)
spiral_direction = EnumProperty(
items=[('COUNTER_CLOCKWISE', "Counter Clockwise",
"Wind in a counter clockwise direction"),
("CLOCKWISE", "Clockwise",
"Wind in a clockwise direction")],
default='COUNTER_CLOCKWISE',
name="Spiral Direction",
description="Direction of winding"
)
turns = IntProperty(
default=1,
min=1, max=1000,
description="Length of Spiral in 360 deg"
)
steps = IntProperty(
default=24,
min=2, max=1000,
description="Number of Vertices per turn"
)
radius = FloatProperty(
default=1.00,
min=0.00, max=100.00,
description="Radius for first turn"
)
dif_z = FloatProperty(
default=0,
min=-10.00, max=100.00,
description="Increase in Z axis per turn"
)
# needed for 1 and 2 spiral_type
# Archemedian variables
dif_radius = FloatProperty(
default=0.00,
min=-50.00, max=50.00,
description="Radius increment in each turn"
)
# step between turns(one turn equals 360 deg)
# Log variables
B_force = FloatProperty(
default=1.00,
min=0.00, max=30.00,
description="Factor of exponent"
)
# Torus variables
inner_radius = FloatProperty(
default=0.20,
min=0.00, max=100,
description="Inner Radius of Torus"
)
dif_inner_radius = FloatProperty(
default=0,
min=-10, max=100,
description="Increase of inner Radius per Cycle"
)
dif_radius = FloatProperty(
default=0,
min=-10, max=100,
description="Increase of Torus Radius per Cycle"
)
cycles = FloatProperty(
default=1,
min=0.00, max=1000,
description="Number of Cycles"
)
curves_number = IntProperty(
default=1,
min=1, max=400,
description="Number of curves of spiral"
)
touch = BoolProperty(
default=False,
description="No empty spaces between cycles"
)
def draw(self, context):
layout = self.layout
col = layout.column_flow(align=True)
col.label("Presets:")
row = col.row(align=True)
row.menu("OBJECT_MT_spiral_curve_presets",
text=bpy.types.OBJECT_MT_spiral_curve_presets.bl_label)
row.operator("curve_extras.spiral_presets", text="", icon='ZOOMIN')
op = row.operator("curve_extras.spiral_presets", text="", icon='ZOOMOUT')
op.remove_active = True
layout.prop(self, "spiral_type")
layout.prop(self, "curve_type")
layout.prop(self, "spiral_direction")
col = layout.column(align=True)
col.label(text="Spiral Parameters:")
col.prop(self, "turns", text="Turns")
col.prop(self, "steps", text="Steps")
box = layout.box()
if self.spiral_type == 'ARCH':
box.label("Archemedian Settings:")
col = box.column(align=True)
col.prop(self, "dif_radius", text="Radius Growth")
col.prop(self, "radius", text="Radius")
col.prop(self, "dif_z", text="Height")
if self.spiral_type == 'LOG':
box.label("Logarithmic Settings:")
col = box.column(align=True)
col.prop(self, "radius", text="Radius")
col.prop(self, "B_force", text="Expansion Force")
col.prop(self, "dif_z", text="Height")
if self.spiral_type == 'SPHERE':
box.label("Spheric Settings:")
box.prop(self, "radius", text="Radius")
if self.spiral_type == 'TORUS':
box.label("Torus Settings:")
col = box.column(align=True)
col.prop(self, "cycles", text="Number of Cycles")
if self.dif_inner_radius == 0 and self.dif_z == 0:
self.cycles = 1
col.prop(self, "radius", text="Radius")
if self.dif_z == 0:
col.prop(self, "dif_z", text="Height per Cycle")
else:
box2 = box.box()
col2 = box2.column(align=True)
col2.prop(self, "dif_z", text="Height per Cycle")
col2.prop(self, "touch", text="Make Snail")
col = box.column(align=True)
col.prop(self, "curves_number", text="Curves Number")
col.prop(self, "inner_radius", text="Inner Radius")
col.prop(self, "dif_radius", text="Increase of Torus Radius")
col.prop(self, "dif_inner_radius", text="Increase of Inner Radius")
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
time_start = time.time()
draw_curve(self, context)
self.report({'INFO'},
"Drawing Spiral Finished: %.4f sec" % (time.time() - time_start))
return {'FINISHED'}
class CURVE_EXTRAS_OT_spirals_presets(AddPresetBase, Operator):
bl_idname = "curve_extras.spiral_presets"
bl_label = "Spirals"
bl_description = "Spirals Presets"
preset_menu = "OBJECT_MT_spiral_curve_presets"
preset_subdir = "curve_extras/curve.spirals"
preset_defines = [
"op = bpy.context.active_operator",
]
preset_values = [
"op.spiral_type",
"op.curve_type",
"op.spiral_direction",
"op.turns",
"op.steps",
"op.radius",
"op.dif_z",
"op.dif_radius",
"op.B_force",
"op.inner_radius",
"op.dif_inner_radius",
"op.cycles",
"op.curves_number",
"op.touch",
]
class OBJECT_MT_spiral_curve_presets(Menu):
'''Presets for curve.spiral'''
bl_label = "Spiral Curve Presets"
bl_idname = "OBJECT_MT_spiral_curve_presets"
preset_subdir = "curve_extras/curve.spirals"
preset_operator = "script.execute_preset"
draw = bpy.types.Menu.draw_preset
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,726 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
"""
bl_info = {
"name": "Torus Knots",
"author": "Marius Giurgi (DolphinDream), testscreenings",
"version": (0, 2),
"blender": (2, 76, 0),
"location": "View3D > Add > Curve",
"description": "Adds many types of (torus) knots",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Curve/Torus_Knot",
"category": "Add Curve"}
"""
import bpy
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
IntProperty
)
from math import (
sin, cos,
pi, sqrt
)
from mathutils import (
Vector,
Matrix,
)
from bpy_extras.object_utils import AddObjectHelper
from random import random
from bpy.types import Operator
# Globals:
DEBUG = False
# greatest common denominator
def gcd(a, b):
if b == 0:
return a
else:
return gcd(b, a % b)
# #######################################################################
# ###################### Knot Definitions ###############################
# #######################################################################
def Torus_Knot(self, linkIndex=0):
p = self.torus_p # revolution count (around the torus center)
q = self.torus_q # spin count (around the torus tube)
N = self.torus_res # curve resolution (number of control points)
# use plus options only when they are enabled
if self.options_plus:
u = self.torus_u # p multiplier
v = self.torus_v # q multiplier
h = self.torus_h # height (scale along Z)
s = self.torus_s # torus scale (radii scale factor)
else: # don't use plus settings
u = 1
v = 1
h = 1
s = 1
R = self.torus_R * s # major radius (scaled)
r = self.torus_r * s # minor radius (scaled)
# number of decoupled links when (p,q) are NOT co-primes
links = gcd(p, q) # = 1 when (p,q) are co-primes
# parametrized angle increment (cached outside of the loop for performance)
# NOTE: the total angle is divided by number of decoupled links to ensure
# the curve does not overlap with itself when (p,q) are not co-primes
da = 2 * pi / links / (N - 1)
# link phase : each decoupled link is phased equally around the torus center
# NOTE: linkIndex value is in [0, links-1]
linkPhase = 2 * pi / q * linkIndex # = 0 when there is just ONE link
# user defined phasing
if self.options_plus:
rPhase = self.torus_rP # user defined revolution phase
sPhase = self.torus_sP # user defined spin phase
else: # don't use plus settings
rPhase = 0
sPhase = 0
rPhase += linkPhase # total revolution phase of the current link
if DEBUG:
print("")
print("Link: %i of %i" % (linkIndex, links))
print("gcd = %i" % links)
print("p = %i" % p)
print("q = %i" % q)
print("link phase = %.2f deg" % (linkPhase * 180 / pi))
print("link phase = %.2f rad" % linkPhase)
# flip directions ? NOTE: flipping both is equivalent to no flip
if self.flip_p:
p *= -1
if self.flip_q:
q *= -1
# create the 3D point array for the current link
newPoints = []
for n in range(N - 1):
# t = 2 * pi / links * n/(N-1) with: da = 2*pi/links/(N-1) => t = n * da
t = n * da
theta = p * t * u + rPhase # revolution angle
phi = q * t * v + sPhase # spin angle
x = (R + r * cos(phi)) * cos(theta)
y = (R + r * cos(phi)) * sin(theta)
z = r * sin(phi) * h
# append 3D point
# NOTE : the array is adjusted later as needed to 4D for POLY and NURBS
newPoints.append([x, y, z])
return newPoints
# ------------------------------------------------------------------------------
# Calculate the align matrix for the new object (based on user preferences)
def align_matrix(self, context):
if self.absolute_location:
loc = Matrix.Translation(Vector((0, 0, 0)))
else:
loc = Matrix.Translation(context.scene.cursor_location)
# user defined location & translation
userLoc = Matrix.Translation(self.location)
userRot = self.rotation.to_matrix().to_4x4()
obj_align = context.user_preferences.edit.object_align
if (context.space_data.type == 'VIEW_3D' and obj_align == 'VIEW'):
rot = context.space_data.region_3d.view_matrix.to_3x3().inverted().to_4x4()
else:
rot = Matrix()
align_matrix = userLoc * loc * rot * userRot
return align_matrix
# ------------------------------------------------------------------------------
# Set curve BEZIER handles to auto
def setBezierHandles(obj, mode='AUTOMATIC'):
scene = bpy.context.scene
if obj.type != 'CURVE':
return
scene.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT', toggle=True)
bpy.ops.curve.select_all(action='SELECT')
bpy.ops.curve.handle_type_set(type=mode)
bpy.ops.object.mode_set(mode='OBJECT', toggle=True)
# ------------------------------------------------------------------------------
# Convert array of vert coordinates to points according to spline type
def vertsToPoints(Verts, splineType):
# main vars
vertArray = []
# array for BEZIER spline output (V3)
if splineType == 'BEZIER':
for v in Verts:
vertArray += v
# array for non-BEZIER output (V4)
else:
for v in Verts:
vertArray += v
if splineType == 'NURBS':
vertArray.append(1) # for NURBS w=1
else: # for POLY w=0
vertArray.append(0)
return vertArray
# ------------------------------------------------------------------------------
# Create the Torus Knot curve and object and add it to the scene
def create_torus_knot(self, context):
# pick a name based on (p,q) parameters
aName = "Torus Knot %i x %i" % (self.torus_p, self.torus_q)
# create curve
curve_data = bpy.data.curves.new(name=aName, type='CURVE')
# setup materials to be used for the TK links
if self.use_colors:
addLinkColors(self, curve_data)
# create torus knot link(s)
if self.multiple_links:
links = gcd(self.torus_p, self.torus_q)
else:
links = 1
for l in range(links):
# get vertices for the current link
verts = Torus_Knot(self, l)
# output splineType 'POLY' 'NURBS' or 'BEZIER'
splineType = self.outputType
# turn verts into proper array (based on spline type)
vertArray = vertsToPoints(verts, splineType)
# create spline from vertArray (based on spline type)
spline = curve_data.splines.new(type=splineType)
if splineType == 'BEZIER':
spline.bezier_points.add(int(len(vertArray) * 1.0 / 3 - 1))
spline.bezier_points.foreach_set('co', vertArray)
else:
spline.points.add(int(len(vertArray) * 1.0 / 4 - 1))
spline.points.foreach_set('co', vertArray)
spline.use_endpoint_u = True
# set curve options
spline.use_cyclic_u = True
spline.order_u = 4
# set a color per link
if self.use_colors:
spline.material_index = l
curve_data.dimensions = '3D'
curve_data.resolution_u = self.segment_res
# create surface ?
if self.geo_surface:
curve_data.fill_mode = 'FULL'
curve_data.bevel_depth = self.geo_bDepth
curve_data.bevel_resolution = self.geo_bRes
curve_data.extrude = self.geo_extrude
curve_data.offset = self.geo_offset
new_obj = bpy.data.objects.new(aName, curve_data)
# set object in the scene
scene = bpy.context.scene
scene.objects.link(new_obj) # place in active scene
new_obj.select = True # set as selected
scene.objects.active = new_obj # set as active
new_obj.matrix_world = self.align_matrix # apply matrix
# set BEZIER handles
if splineType == 'BEZIER':
setBezierHandles(new_obj, self.handleType)
return
# ------------------------------------------------------------------------------
# Create materials to be assigned to each TK link
def addLinkColors(self, curveData):
# some predefined colors for the torus knot links
colors = []
if self.colorSet == "1": # RGBish
colors += [[0.0, 0.0, 1.0]]
colors += [[0.0, 1.0, 0.0]]
colors += [[1.0, 0.0, 0.0]]
colors += [[1.0, 1.0, 0.0]]
colors += [[0.0, 1.0, 1.0]]
colors += [[1.0, 0.0, 1.0]]
colors += [[1.0, 0.5, 0.0]]
colors += [[0.0, 1.0, 0.5]]
colors += [[0.5, 0.0, 1.0]]
else: # RainBow
colors += [[0.0, 0.0, 1.0]]
colors += [[0.0, 0.5, 1.0]]
colors += [[0.0, 1.0, 1.0]]
colors += [[0.0, 1.0, 0.5]]
colors += [[0.0, 1.0, 0.0]]
colors += [[0.5, 1.0, 0.0]]
colors += [[1.0, 1.0, 0.0]]
colors += [[1.0, 0.5, 0.0]]
colors += [[1.0, 0.0, 0.0]]
me = curveData
links = gcd(self.torus_p, self.torus_q)
for i in range(links):
matName = "TorusKnot-Link-%i" % i
matListNames = bpy.data.materials.keys()
# create the material
if matName not in matListNames:
if DEBUG:
print("Creating new material : %s" % matName)
mat = bpy.data.materials.new(matName)
else:
if DEBUG:
print("Material %s already exists" % matName)
mat = bpy.data.materials[matName]
# set material color
if self.options_plus and self.random_colors:
mat.diffuse_color = random(), random(), random()
else:
cID = i % (len(colors)) # cycle through predefined colors
mat.diffuse_color = colors[cID]
if self.options_plus:
mat.diffuse_color.s = self.saturation
else:
mat.diffuse_color.s = 0.75
me.materials.append(mat)
# ------------------------------------------------------------------------------
# Main Torus Knot class
class torus_knot_plus(Operator, AddObjectHelper):
bl_idname = "curve.torus_knot_plus"
bl_label = "Torus Knot +"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
bl_description = "Adds many types of tours knots"
bl_context = "object"
def mode_update_callback(self, context):
# keep the equivalent radii sets (R,r)/(eR,iR) in sync
if self.mode == 'EXT_INT':
self.torus_eR = self.torus_R + self.torus_r
self.torus_iR = self.torus_R - self.torus_r
# align_matrix for the invoke
align_matrix = None
# GENERAL options
options_plus = BoolProperty(
name="Extra Options",
default=False,
description="Show more options (the plus part)",
)
absolute_location = BoolProperty(
name="Absolute Location",
default=False,
description="Set absolute location instead of relative to 3D cursor",
)
# COLOR options
use_colors = BoolProperty(
name="Use Colors",
default=False,
description="Show torus links in colors",
)
colorSet = EnumProperty(
name="Color Set",
items=(('1', "RGBish", "RGBsish ordered colors"),
('2', "Rainbow", "Rainbow ordered colors")),
)
random_colors = BoolProperty(
name="Randomize Colors",
default=False,
description="Randomize link colors",
)
saturation = FloatProperty(
name="Saturation",
default=0.75,
min=0.0, max=1.0,
description="Color saturation",
)
# SURFACE Options
geo_surface = BoolProperty(
name="Surface",
default=True,
description="Create surface",
)
geo_bDepth = FloatProperty(
name="Bevel Depth",
default=0.04,
min=0, soft_min=0,
description="Bevel Depth",
)
geo_bRes = IntProperty(
name="Bevel Resolution",
default=2,
min=0, soft_min=0,
max=5, soft_max=5,
description="Bevel Resolution"
)
geo_extrude = FloatProperty(
name="Extrude",
default=0.0,
min=0, soft_min=0,
description="Amount of curve extrusion"
)
geo_offset = FloatProperty(
name="Offset",
default=0.0,
min=0, soft_min=0,
description="Offset the surface relative to the curve"
)
# TORUS KNOT Options
torus_p = IntProperty(
name="p",
default=2,
min=1, soft_min=1,
description="Number of Revolutions around the torus hole before closing the knot"
)
torus_q = IntProperty(
name="q",
default=3,
min=1, soft_min=1,
description="Number of Spins through the torus hole before closing the knot"
)
flip_p = BoolProperty(
name="Flip p",
default=False,
description="Flip Revolution direction"
)
flip_q = BoolProperty(
name="Flip q",
default=False,
description="Flip Spin direction"
)
multiple_links = BoolProperty(
name="Multiple Links",
default=True,
description="Generate all links or just one link when q and q are not co-primes"
)
torus_u = IntProperty(
name="Rev. Multiplier",
default=1,
min=1, soft_min=1,
description="Revolutions Multiplier"
)
torus_v = IntProperty(
name="Spin Multiplier",
default=1,
min=1, soft_min=1,
description="Spin multiplier"
)
torus_rP = FloatProperty(
name="Revolution Phase",
default=0.0,
min=0.0, soft_min=0.0,
description="Phase revolutions by this radian amount"
)
torus_sP = FloatProperty(
name="Spin Phase",
default=0.0,
min=0.0, soft_min=0.0,
description="Phase spins by this radian amount"
)
# TORUS DIMENSIONS options
mode = EnumProperty(
name="Torus Dimensions",
items=(("MAJOR_MINOR", "Major/Minor",
"Use the Major/Minor radii for torus dimensions."),
("EXT_INT", "Exterior/Interior",
"Use the Exterior/Interior radii for torus dimensions.")),
update=mode_update_callback,
)
torus_R = FloatProperty(
name="Major Radius",
min=0.00, max=100.0,
default=1.0,
subtype='DISTANCE',
unit='LENGTH',
description="Radius from the torus origin to the center of the cross section"
)
torus_r = FloatProperty(
name="Minor Radius",
min=0.00, max=100.0,
default=.25,
subtype='DISTANCE',
unit='LENGTH',
description="Radius of the torus' cross section"
)
torus_iR = FloatProperty(
name="Interior Radius",
min=0.00, max=100.0,
default=.75,
subtype='DISTANCE',
unit='LENGTH',
description="Interior radius of the torus (closest to the torus center)"
)
torus_eR = FloatProperty(
name="Exterior Radius",
min=0.00, max=100.0,
default=1.25,
subtype='DISTANCE',
unit='LENGTH',
description="Exterior radius of the torus (farthest from the torus center)"
)
torus_s = FloatProperty(
name="Scale",
min=0.01, max=100.0,
default=1.00,
description="Scale factor to multiply the radii"
)
torus_h = FloatProperty(
name="Height",
default=1.0,
min=0.0, max=100.0,
description="Scale along the local Z axis"
)
# CURVE options
torus_res = IntProperty(
name="Curve Resolution",
default=100,
min=3, soft_min=3,
description="Number of control vertices in the curve"
)
segment_res = IntProperty(
name="Segment Resolution",
default=12,
min=1, soft_min=1,
description="Curve subdivisions per segment"
)
SplineTypes = [
('POLY', "Poly", "Poly type"),
('NURBS', "Nurbs", "Nurbs type"),
('BEZIER', "Bezier", "Bezier type")]
outputType = EnumProperty(
name="Output splines",
default='BEZIER',
description="Type of splines to output",
items=SplineTypes,
)
bezierHandles = [
('VECTOR', "Vector", "Bezier Hanles type - Vector"),
('AUTOMATIC', "Auto", "Bezier Hanles type - Automatic"),
]
handleType = EnumProperty(
name="Handle type",
default='AUTOMATIC',
items=bezierHandles,
description="Bezier handle type",
)
adaptive_resolution = BoolProperty(
name="Adaptive Resolution",
default=False,
description="Auto adjust curve resolution based on TK length",
)
def draw(self, context):
layout = self.layout
# extra parameters toggle
layout.prop(self, "options_plus")
# TORUS KNOT Parameters
col = layout.column()
col.label(text="Torus Knot Parameters:")
box = layout.box()
split = box.split(percentage=0.85, align=True)
split.prop(self, "torus_p", text="Revolutions")
split.prop(self, "flip_p", toggle=True, text="",
icon="ARROW_LEFTRIGHT")
split = box.split(percentage=0.85, align=True)
split.prop(self, "torus_q", text="Spins")
split.prop(self, "flip_q", toggle=True, text="",
icon="ARROW_LEFTRIGHT")
links = gcd(self.torus_p, self.torus_q)
info = "Multiple Links"
if links > 1:
info += " ( " + str(links) + " )"
box.prop(self, 'multiple_links', text=info)
if self.options_plus:
box = box.box()
col = box.column(align=True)
col.prop(self, "torus_u")
col.prop(self, "torus_v")
col = box.column(align=True)
col.prop(self, "torus_rP")
col.prop(self, "torus_sP")
# TORUS DIMENSIONS options
col = layout.column(align=True)
col.label(text="Torus Dimensions:")
box = layout.box()
col = box.column(align=True)
col.row().prop(self, "mode", expand=True)
if self.mode == "MAJOR_MINOR":
col = box.column(align=True)
col.prop(self, "torus_R")
col.prop(self, "torus_r")
else: # EXTERIOR-INTERIOR
col = box.column(align=True)
col.prop(self, "torus_eR")
col.prop(self, "torus_iR")
if self.options_plus:
box = box.box()
col = box.column(align=True)
col.prop(self, "torus_s")
col.prop(self, "torus_h")
# CURVE options
col = layout.column(align=True)
col.label(text="Curve Options:")
box = layout.box()
col = box.column()
col.label(text="Output Curve Type:")
col.row().prop(self, "outputType", expand=True)
depends = box.column()
depends.prop(self, "torus_res")
# deactivate the "curve resolution" if "adaptive resolution" is enabled
depends.enabled = not self.adaptive_resolution
box.prop(self, "adaptive_resolution")
box.prop(self, "segment_res")
# SURFACE options
col = layout.column()
col.label(text="Geometry Options:")
box = layout.box()
box.prop(self, "geo_surface")
if self.geo_surface:
col = box.column(align=True)
col.prop(self, "geo_bDepth")
col.prop(self, "geo_bRes")
col = box.column(align=True)
col.prop(self, "geo_extrude")
col.prop(self, "geo_offset")
# COLOR options
col = layout.column()
col.label(text="Color Options:")
box = layout.box()
box.prop(self, "use_colors")
if self.use_colors and self.options_plus:
box = box.box()
box.prop(self, "colorSet")
box.prop(self, "random_colors")
box.prop(self, "saturation")
# TRANSFORM options
col = layout.column()
col.label(text="Transform Options:")
box = col.box()
box.prop(self, "location")
box.prop(self, "absolute_location")
box.prop(self, "rotation")
@classmethod
def poll(cls, context):
if context.mode != "OBJECT":
return False
return context.scene is not None
def execute(self, context):
if self.mode == 'EXT_INT':
# adjust the equivalent radii pair : (R,r) <=> (eR,iR)
self.torus_R = (self.torus_eR + self.torus_iR) * 0.5
self.torus_r = (self.torus_eR - self.torus_iR) * 0.5
if self.adaptive_resolution:
# adjust curve resolution automatically based on (p,q,R,r) values
p = self.torus_p
q = self.torus_q
R = self.torus_R
r = self.torus_r
links = gcd(p, q)
# get an approximate length of the whole TK curve
# upper bound approximation
maxTKLen = 2 * pi * sqrt(p * p * (R + r) * (R + r) + q * q * r * r)
# lower bound approximation
minTKLen = 2 * pi * sqrt(p * p * (R - r) * (R - r) + q * q * r * r)
avgTKLen = (minTKLen + maxTKLen) / 2 # average approximation
if DEBUG:
print("Approximate average TK length = %.2f" % avgTKLen)
# x N factor = control points per unit length
self.torus_res = max(3, avgTKLen / links * 8)
# update align matrix
self.align_matrix = align_matrix(self, context)
# turn off undo
undo = bpy.context.user_preferences.edit.use_global_undo
bpy.context.user_preferences.edit.use_global_undo = False
# create the curve
create_torus_knot(self, context)
# restore pre operator undo state
bpy.context.user_preferences.edit.use_global_undo = undo
return {'FINISHED'}
def invoke(self, context, event):
self.execute(context)
return {'FINISHED'}

View File

@@ -0,0 +1,398 @@
# gpl: author Folkert de Vries
bl_info = {
"name": "Surface: Plane / Cone/ Star / Wedge",
"description": "Create a NURBS surface plane",
"author": "Folkert de Vries",
"version": (1, 0, 1),
"blender": (2, 5, 9),
"location": "View3D > Add > Surface",
"warning": "",
"wiki_url": "",
"category": "Add Mesh"
}
"""
Info:
to add a surface star, plane or cone, go to add Menu > Surface > Star, Plane or Cone
next parameters like scale and u and v resolution can be adjusted in the toolshelf
have fun using this add-on
"""
import bpy
from bpy.props import (
FloatProperty,
IntProperty,
)
from bpy.types import Operator
# generic class for inheritance
class MakeSurfaceHelpers:
# get input for size and resolution
size = FloatProperty(
name="Size",
description="Size of the object",
default=1.0,
min=0.01,
max=100.0,
unit="LENGTH",
)
res_u = IntProperty(
name="Resolution U",
description="Surface resolution in u direction",
default=1,
min=1,
max=500,
)
res_v = IntProperty(
name="Resolution V",
description="Surface resolution in v direction",
default=1,
min=1,
max=500,
)
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
def draw(self, context):
layout = self.layout
layout.prop(self, "size")
col = layout.column(align=True)
col.prop(self, "res_u")
col.prop(self, "res_v")
class MakeSurfaceWedge(Operator, MakeSurfaceHelpers):
bl_idname = "object.add_surface_wedge"
bl_label = "Add Surface Wedge"
bl_description = "Construct a Surface Wedge"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
# variables
size = self.size
res_u = self.res_u
res_v = self.res_v
# add a surface Plane
bpy.ops.object.add_surface_plane()
# save some time, by getting instant acces to those values
ao = context.active_object
point = ao.data.splines[0].points
# rotate 90 degrees on the z axis
ao.rotation_euler[0] = 0.0
ao.rotation_euler[1] = 0.0
ao.rotation_euler[2] = 1.570796
# go into edit mode and deselect
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.curve.select_all(action='DESELECT')
# select points 0 and 1, and extrudde them
# declaring ao and point again seems necesary...
ao = context.active_object
point = ao.data.splines[0].points
point[0].select = True
ao = context.active_object
point = ao.data.splines[0].points
point[1].select = True
bpy.ops.curve.extrude()
# bring extruded points up 1 bu on the z axis, and toggle
# cyclic in V direction
bpy.ops.transform.translate(value=(0, 0, 1), constraint_axis=(False, False, True))
bpy.ops.curve.cyclic_toggle(direction='CYCLIC_V')
# get points to the right coords.
point[0].co = (1.0, 0.0, 1.0, 1.0)
point[1].co = (-1.0, 0.0, 1.0, 1.0)
point[2].co = (1.0, -0.5, 0.0, 1.0)
point[3].co = (-1.0, -0.5, 0.0, 1.0)
point[4].co = (1.0, 0.5, 0.0, 1.0)
point[5].co = (-1.0, 0.5, 0.0, 1.0)
# go back to object mode
bpy.ops.object.mode_set(mode='OBJECT')
# get origin to geometry.
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
# change name
context.active_object.name = 'SurfaceWedge'
# get the wedge to the 3d cursor.
context.active_object.location = context.scene.cursor_location
bpy.ops.transform.resize(value=(size, size, size))
# adjust resolution in u and v direction
context.active_object.data.resolution_u = res_u
context.active_object.data.resolution_v = res_v
return{'FINISHED'}
class MakeSurfaceCone(Operator, MakeSurfaceHelpers):
bl_idname = "object.add_surface_cone"
bl_label = "Add Surface Cone"
bl_description = "Construct a Surface Cone"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
size = self.size
res_u = self.res_u
res_v = self.res_v
# add basemesh, a nurbs torus
bpy.ops.surface.primitive_nurbs_surface_torus_add(location=(0, 0, 0))
# get active object and active object name
ao = context.active_object
# go to edit mode
bpy.ops.object.mode_set(mode='EDIT')
# deselect all
bpy.ops.curve.select_all(action='DESELECT')
# too shorten alot of lines
point = ao.data.splines[0].points
# get middle points
for i in range(0, 63):
if point[i].co.z == 0.0:
point[i].select = True
# select non-middle points and delete them
bpy.ops.curve.select_all(action='INVERT')
bpy.ops.curve.delete(type='VERT')
# declaring this again seems necesary...
point = ao.data.splines[0].points
# list of points to be in center, and 2 bu'' s higher
ToKeep = [1, 3, 5, 7, 9, 11, 13, 15, 17]
for i in range(0, len(ToKeep) - 1):
point[ToKeep[i]].select = True
bpy.ops.transform.resize(value=(0, 0, 0))
bpy.ops.curve.cyclic_toggle(direction='CYCLIC_U')
bpy.ops.transform.translate(value=(0, 0, 2))
# to make cone visible
bpy.ops.object.editmode_toggle()
bpy.ops.object.editmode_toggle()
# change name
context.active_object.name = 'SurfaceCone'
# go back to object mode
bpy.ops.object.editmode_toggle()
# bring object to cursor
bpy.ops.object.mode_set(mode='OBJECT')
context.active_object.location = context.scene.cursor_location
# adjust size
bpy.ops.transform.resize(value=(size, size, size))
# adjust resolution in u and v direction
context.active_object.data.resolution_u = res_u
context.active_object.data.resolution_v = res_v
return{'FINISHED'}
class MakeSurfaceStar(Operator, MakeSurfaceHelpers):
bl_idname = "object.add_surface_star"
bl_label = "Add Surface Star"
bl_description = "Contruct a Surface Star"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
size = self.size
res_u = self.res_u
res_v = self.res_v
# add surface circle:
bpy.ops.surface.primitive_nurbs_surface_circle_add(location=(0, 0, 0))
# we got 8 points, we need 40 points.
# get active object
ao = context.active_object
# enter edtimode
bpy.ops.object.mode_set(mode='EDIT')
# deselect all
bpy.ops.curve.select_all(action='DESELECT')
# select point 0 and 1, and subdivide
point = ao.data.splines[0].points
point[0].select = True
point[1].select = True
bpy.ops.curve.subdivide()
bpy.ops.curve.select_all(action='DESELECT')
# select point 2 and 3, and subdivide
point[2].select = True
point[3].select = True
bpy.ops.curve.subdivide()
bpy.ops.curve.select_all(action='DESELECT')
ListOfCoords = [
(0.5, 0.0, 0.25, 1.0),
(0.80901700258255, 0.5877853035926819, 0.25, 1.0),
(0.1545085906982422, 0.4755282402038574, 0.25, 1.0),
(-0.30901703238487244, 0.9510565400123596, 0.25, 1.0),
(-0.4045085608959198, 0.293892502784729, 0.2499999850988388, 1.0),
(-1.0, 0.0, 0.25, 1.0),
(-0.4045085608959198, -0.293892502784729, 0.2499999850988388, 1.0),
(-0.30901703238487244, -0.9510565400123596, 0.25, 1.0),
(0.1545085906982422, -0.4755282402038574, 0.25, 1.0),
(0.8090166449546814, -0.5877856612205505, 0.2499999850988388, 1.0)
]
for i in range(0, 10):
context.active_object.data.splines[0].points[i].co = ListOfCoords[i]
# now select all, and subdivide till 40 points is reached:
bpy.ops.curve.select_all(action='SELECT')
bpy.ops.curve.subdivide()
bpy.ops.curve.subdivide()
bpy.ops.curve.subdivide()
# extrude the star
bpy.ops.curve.extrude(mode='TRANSLATION')
# bring extruded part up
bpy.ops.transform.translate(
value=(0, 0, 0.5),
constraint_axis=(False, False, True)
)
# flip normals
bpy.ops.curve.switch_direction()
# go back to object mode
bpy.ops.object.mode_set(mode='OBJECT')
# origin to geometry
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
# get object to 3d cursor
context.active_object.location = context.scene.cursor_location
# change name
ao.name = 'SurfaceStar'
# adjust size
bpy.ops.transform.resize(value=(size, size, size))
# adjust resolution in u and v direction
context.active_object.data.resolution_u = res_u
context.active_object.data.resolution_v = res_v
return{'FINISHED'}
class MakeSurfacePlane(Operator, MakeSurfaceHelpers):
bl_idname = "object.add_surface_plane"
bl_label = "Add Surface Plane"
bl_description = "Contruct a Surface Plane"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
size = self.size
res_u = self.res_u
res_v = self.res_v
bpy.ops.surface.primitive_nurbs_surface_surface_add() # add the base mesh, a NURBS Surface
bpy.ops.transform.resize(
value=(1, 1, 0.0001),
constraint_axis=(False, False, True)
) # make it flat
# added surface has 16 points
# deleting points to get plane shape
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.curve.select_all(action='DESELECT')
context.active_object.data.splines[0].points[0].select = True
context.active_object.data.splines[0].points[1].select = True
context.active_object.data.splines[0].points[2].select = True
context.active_object.data.splines[0].points[3].select = True
bpy.ops.curve.delete(type='VERT')
context.active_object.data.splines[0].points[8].select = True
context.active_object.data.splines[0].points[9].select = True
context.active_object.data.splines[0].points[10].select = True
context.active_object.data.splines[0].points[11].select = True
bpy.ops.curve.delete(type='VERT')
context.active_object.data.splines[0].points[0].select = True
context.active_object.data.splines[0].points[4].select = True
bpy.ops.curve.delete(type='VERT')
context.active_object.data.splines[0].points[2].select = True
context.active_object.data.splines[0].points[5].select = True
bpy.ops.curve.delete(type='VERT')
# assigning name
context.active_object.name = "SurfacePlane"
# select all
bpy.ops.curve.select_all(action='SELECT')
# bringing origin to center:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
# transform scale
bpy.ops.object.transform_apply(scale=True)
# bring object to 3d cursor
bpy.ops.object.mode_set(mode='OBJECT')
context.active_object.location = context.scene.cursor_location
bpy.ops.transform.resize(value=(size, size, size))
# adjust resolution in u and v direction
context.active_object.data.resolution_u = res_u
context.active_object.data.resolution_v = res_v
return{'FINISHED'}
class SmoothXtimes(Operator):
bl_idname = "curve.smooth_x_times"
bl_label = "Smooth X Times"
bl_space_type = "VIEW_3D"
bl_options = {'REGISTER', 'UNDO'}
# use of this class:
# lets you smooth till a thousand times. this is normally difficult, because
# you have to press w, click, press w, click etc.
# get values
times = IntProperty(
name="Smooth x Times",
min=1,
max=1000,
default=1,
description="Smooth amount"
)
@classmethod
def poll(cls, context):
return context.mode == 'EDIT_SURFACE'
def execute(self, context):
# smooth times
times = self.times
for i in range(1, times):
bpy.ops.curve.smooth()
return{'FINISHED'}
def register():
bpy.utils.register_class(MakeSurfaceHelpers)
bpy.utils.register_class(MakeSurfacePlane)
bpy.utils.register_class(MakeSurfaceCone)
bpy.utils.register_class(MakeSurfaceStar)
bpy.utils.register_class(MakeSurfaceWedge)
bpy.utils.register_class(SmoothXtimes)
def unregister():
bpy.utils.unregister_class(MakeSurfaceHelpers)
bpy.utils.unregister_class(MakeSurfacePlane)
bpy.utils.unregister_class(MakeSurfaceCone)
bpy.utils.unregister_class(MakeSurfaceStar)
bpy.utils.unregister_class(MakeSurfaceWedge)
bpy.utils.unregister_class(SmoothXtimes)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,422 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
# DevBo Task: https://developer.blender.org/T37377
bl_info = {
"name": "Bevel/Taper Curve",
"author": "Cmomoney",
"version": (1, 1),
"blender": (2, 69, 0),
"location": "View3D > Object > Bevel/Taper",
"description": "Adds bevel and/or taper curve to active curve",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
"Py/Scripts/Curve/Bevel_-Taper_Curve",
"category": "Curve"}
import bpy
from bpy.types import (
Operator,
Menu,
)
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
)
from bpy_extras.object_utils import (
AddObjectHelper,
object_data_add,
)
def add_taper(self, context):
scale_ends1 = self.scale_ends1
scale_ends2 = self.scale_ends2
scale_mid = self.scale_mid
verts = [
(-2.0, 1.0 * scale_ends1, 0.0, 1.0),
(-1.0, 0.75 * scale_mid, 0.0, 1.0),
(0.0, 1.5 * scale_mid, 0.0, 1.0),
(1.0, 0.75 * scale_mid, 0.0, 1.0),
(2.0, 1.0 * scale_ends2, 0.0, 1.0)
]
make_path(self, context, verts)
def add_type5(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[0.0 * scale_x, 0.049549 * scale_y,
0.0, 0.031603 * scale_x, 0.047013 * scale_y,
0.0, 0.05 * scale_x, 0.0 * scale_y, 0.0,
0.031603 * scale_x, -0.047013 * scale_y,
0.0, 0.0 * scale_x, -0.049549 * scale_y,
0.0, -0.031603 * scale_x, -0.047013 * scale_y,
0.0, -0.05 * scale_x, -0.0 * scale_y, 0.0,
-0.031603 * scale_x, 0.047013 * scale_y, 0.0]
]
lhandles = [
[(-0.008804 * scale_x, 0.049549 * scale_y, 0.0),
(0.021304 * scale_x, 0.02119 * scale_y, 0.0),
(0.05 * scale_x, 0.051228 * scale_y, 0.0),
(0.036552 * scale_x, -0.059423 * scale_y, 0.0),
(0.008804 * scale_x, -0.049549 * scale_y, 0.0),
(-0.021304 * scale_x, -0.02119 * scale_y, 0.0),
(-0.05 * scale_x, -0.051228 * scale_y, 0.0),
(-0.036552 * scale_x, 0.059423 * scale_y, 0.0)]
]
rhandles = [
[(0.008803 * scale_x, 0.049549 * scale_y, 0.0),
(0.036552 * scale_x, 0.059423 * scale_y, 0.0),
(0.05 * scale_x, -0.051228 * scale_y, 0.0),
(0.021304 * scale_x, -0.02119 * scale_y, 0.0),
(-0.008803 * scale_x, -0.049549 * scale_y, 0.0),
(-0.036552 * scale_x, -0.059423 * scale_y, 0.0),
(-0.05 * scale_x, 0.051228 * scale_y, 0.0),
(-0.021304 * scale_x, 0.02119 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type4(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.0 * scale_x, 0.017183 * scale_y,
0.0, 0.05 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, -0.017183 * scale_y,
0.0, -0.05 * scale_x, -0.0 * scale_y, 0.0]
]
lhandles = [
[(-0.017607 * scale_x, 0.017183 * scale_y, 0.0),
(0.05 * scale_x, 0.102456 * scale_y, 0.0),
(0.017607 * scale_x, -0.017183 * scale_y, 0.0),
(-0.05 * scale_x, -0.102456 * scale_y, 0.0)]
]
rhandles = [
[(0.017607 * scale_x, 0.017183 * scale_y, 0.0),
(0.05 * scale_x, -0.102456 * scale_y, 0.0),
(-0.017607 * scale_x, -0.017183 * scale_y, 0.0),
(-0.05 * scale_x, 0.102456 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type3(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.017183 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, 0.05 * scale_y,
0.0, 0.017183 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, -0.05 * scale_y, 0.0]
]
lhandles = [
[(-0.017183 * scale_x, -0.017607 * scale_y, 0.0),
(-0.102456 * scale_x, 0.05 * scale_y, 0.0),
(0.017183 * scale_x, 0.017607 * scale_y, 0.0),
(0.102456 * scale_x, -0.05 * scale_y, 0.0)]
]
rhandles = [
[(-0.017183 * scale_x, 0.017607 * scale_y, 0.0),
(0.102456 * scale_x, 0.05 * scale_y, 0.0),
(0.017183 * scale_x, -0.017607 * scale_y, 0.0),
(-0.102456 * scale_x, -0.05 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type2(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.05 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, 0.05 * scale_y,
0.0, 0.05 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, -0.05 * scale_y, 0.0]
]
lhandles = [
[(-0.05 * scale_x, -0.047606 * scale_y, 0.0),
(-0.047606 * scale_x, 0.05 * scale_y, 0.0),
(0.05 * scale_x, 0.047607 * scale_y, 0.0),
(0.047606 * scale_x, -0.05 * scale_y, 0.0)]
]
rhandles = [
[(-0.05 * scale_x, 0.047607 * scale_y, 0.0),
(0.047607 * scale_x, 0.05 * scale_y, 0.0),
(0.05 * scale_x, -0.047607 * scale_y, 0.0),
(-0.047607 * scale_x, -0.05 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def add_type1(self, context):
scale_x = self.scale_x
scale_y = self.scale_y
verts = [
[-0.05 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, 0.05 * scale_y,
0.0, 0.05 * scale_x, 0.0 * scale_y,
0.0, 0.0 * scale_x, -0.05 * scale_y, 0.0]
]
lhandles = [
[(-0.05 * scale_x, -0.027606 * scale_y, 0.0),
(-0.027606 * scale_x, 0.05 * scale_y, 0.0),
(0.05 * scale_x, 0.027606 * scale_y, 0.0),
(0.027606 * scale_x, -0.05 * scale_y, 0.0)]
]
rhandles = [
[(-0.05 * scale_x, 0.027607 * scale_y, 0.0),
(0.027607 * scale_x, 0.05 * scale_y, 0.0),
(0.05 * scale_x, -0.027607 * scale_y, 0.0),
(-0.027607 * scale_x, -0.05 * scale_y, 0.0)]
]
make_curve(self, context, verts, lhandles, rhandles)
def make_path(self, context, verts):
target = bpy.context.scene.objects.active
bpy.ops.curve.primitive_nurbs_path_add(
view_align=False, enter_editmode=False, location=(0, 0, 0)
)
target.data.taper_object = bpy.context.scene.objects.active
taper = bpy.context.scene.objects.active
taper.name = target.name + '_Taper'
bpy.context.scene.objects.active = target
points = taper.data.splines[0].points
for i in range(len(verts)):
points[i].co = verts[i]
def make_curve(self, context, verts, lh, rh):
target = bpy.context.scene.objects.active
curve_data = bpy.data.curves.new(
name=target.name + '_Bevel', type='CURVE'
)
curve_data.dimensions = '3D'
for p in range(len(verts)):
c = 0
spline = curve_data.splines.new(type='BEZIER')
spline.use_cyclic_u = True
spline.bezier_points.add(len(verts[p]) / 3 - 1)
spline.bezier_points.foreach_set('co', verts[p])
for bp in spline.bezier_points:
bp.handle_left_type = 'ALIGNED'
bp.handle_right_type = 'ALIGNED'
bp.handle_left.xyz = lh[p][c]
bp.handle_right.xyz = rh[p][c]
c += 1
object_data_add(context, curve_data, operator=self)
target.data.bevel_object = bpy.context.scene.objects.active
bpy.context.scene.objects.active = target
class add_tapercurve(Operator):
bl_idname = "curve.tapercurve"
bl_label = "Add Curve as Taper"
bl_description = ("Add taper curve to Active Curve\n"
"Needs an existing Active Curve")
bl_options = {'REGISTER', 'UNDO'}
scale_ends1 = FloatProperty(
name="End Width Left",
description="Adjust left end taper",
default=0.0,
min=0.0
)
scale_ends2 = FloatProperty(
name="End Width Right",
description="Adjust right end taper",
default=0.0,
min=0.0
)
scale_mid = FloatProperty(
name="Center Width",
description="Adjust taper at center",
default=1.0,
min=0.0
)
link1 = BoolProperty(
name="Link Ends",
description="Link the End Width Left / Right settings\n"
"End Width Left will be editable ",
default=True
)
link2 = BoolProperty(
name="Link Ends / Center",
description="Link the End Widths with the Center Width",
default=False
)
diff = FloatProperty(
name="Difference",
default=1,
description="Difference between ends and center while linked"
)
@classmethod
def poll(cls, context):
obj = context.active_object
return context.mode == 'OBJECT' and obj and obj.type == "CURVE"
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.label("Settings:")
split = layout.split(percentage=0.95, align=True)
split.active = not self.link2
col = split.column(align=True)
col.prop(self, "scale_ends1")
row = split.row(align=True)
row.scale_y = 2.0
col_sub = col.column(align=True)
col_sub.active = not self.link1
col_sub.prop(self, "scale_ends2")
row.prop(self, "link1", toggle=True, text="", icon="LINKED")
split = layout.split(percentage=0.95, align=True)
col = split.column(align=True)
col.prop(self, "scale_mid")
row = split.row(align=True)
row.scale_y = 2.0
col_sub = col.column(align=True)
col_sub.active = self.link2
row.prop(self, "link2", toggle=True, text="", icon="LINKED")
col_sub.prop(self, "diff")
def execute(self, context):
if self.link1:
self.scale_ends2 = self.scale_ends1
if self.link2:
self.scale_ends2 = self.scale_ends1 = self.scale_mid - self.diff
add_taper(self, context)
return {'FINISHED'}
class add_bevelcurve(Operator, AddObjectHelper):
bl_idname = "curve.bevelcurve"
bl_label = "Add Curve as Bevel"
bl_description = ("Add bevel curve to Active Curve\n"
"Needs an existing Active Curve")
bl_options = {'REGISTER', 'UNDO'}
types = IntProperty(
name="Type",
description="Type of bevel curve",
default=1,
min=1, max=5
)
scale_x = FloatProperty(
name="Scale X",
description="Scale on X axis",
default=1.0
)
scale_y = FloatProperty(
name="Scale Y",
description="Scale on Y axis",
default=1.0
)
link = BoolProperty(
name="Link XY",
description="Link the Scale on X/Y axis",
default=True
)
@classmethod
def poll(cls, context):
obj = context.active_object
return context.mode == 'OBJECT' and obj and obj.type == "CURVE"
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
# AddObjectHelper props
col.prop(self, "view_align")
col.prop(self, "location")
col.prop(self, "rotation")
col = layout.column(align=True)
col.label("Settings:")
col.prop(self, "types")
split = layout.split(percentage=0.95, align=True)
col = split.column(align=True)
col.prop(self, "scale_x")
row = split.row(align=True)
row.scale_y = 2.0
col.prop(self, "scale_y")
row.prop(self, "link", toggle=True, text="", icon="LINKED")
def execute(self, context):
if self.link:
self.scale_y = self.scale_x
if self.types == 1:
add_type1(self, context)
if self.types == 2:
add_type2(self, context)
if self.types == 3:
add_type3(self, context)
if self.types == 4:
add_type4(self, context)
if self.types == 5:
add_type5(self, context)
return {'FINISHED'}
class Bevel_Taper_Curve_Menu(Menu):
bl_label = "Bevel/Taper"
bl_idname = "OBJECT_MT_bevel_taper_curve_menu"
def draw(self, context):
layout = self.layout
layout.operator("curve.bevelcurve")
layout.operator("curve.tapercurve")
def menu_funcs(self, context):
if bpy.context.scene.objects.active.type == "CURVE":
self.layout.menu("OBJECT_MT_bevel_taper_curve_menu")
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_object.append(menu_funcs)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.VIEW3D_MT_object.remove(menu_funcs)
if __name__ == "__main__":
register()