More unit tests
Copy in a multifile addon for testing, also fix a typo in blenderpack.py
This commit is contained in:
@@ -58,7 +58,7 @@ def extract_blinfo(path):
|
|||||||
addon_name = os.path.split(path.rstrip(os.path.sep))[1]
|
addon_name = os.path.split(path.rstrip(os.path.sep))[1]
|
||||||
|
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
with open(os.path.join(path, '__init__.py' 'r')) as f:
|
with open(os.path.join(path, '__init__.py'), 'r') as f:
|
||||||
source = f.read()
|
source = f.read()
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
@@ -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')))
|
reality = str(blenderpack.extract_blinfo(os.path.join(self.helper_path, 'addons', 'add_curve_extra_objects.zip')))
|
||||||
self.assertEqual(expectation, reality)
|
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):
|
# def test_validpath(self):
|
||||||
# blenderpack.make_repo(os.path.join('test_helpers', 'addons'))
|
# blenderpack.make_repo(os.path.join('test_helpers', 'addons'))
|
||||||
|
|
||||||
|
301
tests/test_helpers/addons/add_curve_extra_objects/__init__.py
Normal file
301
tests/test_helpers/addons/add_curve_extra_objects/__init__.py
Normal 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
@@ -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()
|
@@ -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()
|
@@ -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
@@ -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()
|
File diff suppressed because it is too large
Load Diff
@@ -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'}
|
@@ -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()
|
@@ -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()
|
Reference in New Issue
Block a user