Clean up tests
No need to test on complete addons. This commit adds a few test cases and some automated test generation.
This commit is contained in:
@@ -7,38 +7,56 @@ import blenderpack
|
|||||||
class test_blenderpack_make_repo(unittest.TestCase):
|
class test_blenderpack_make_repo(unittest.TestCase):
|
||||||
|
|
||||||
helper_path = os.path.join('tests', 'test_helpers')
|
helper_path = os.path.join('tests', 'test_helpers')
|
||||||
|
addon_path = os.path.join(helper_path, 'addons')
|
||||||
def test_extract_blinfo_from_file(self):
|
|
||||||
with open(os.path.join(self.helper_path, 'ivy_gen_blinfo.txt'), 'r') as f:
|
|
||||||
expectation = f.read()
|
|
||||||
|
|
||||||
reality = str(blenderpack.extract_blinfo(os.path.join(self.helper_path, 'addons', 'add_curve_ivygen.py')))
|
|
||||||
self.assertEqual(expectation, reality)
|
|
||||||
|
|
||||||
def test_extract_blinfo_from_zip(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.zip')))
|
|
||||||
self.assertEqual(expectation, reality)
|
|
||||||
|
|
||||||
def test_extract_blinfo_from_dir(self):
|
|
||||||
with open(os.path.join(self.helper_path, 'extra_objects_blinfo.txt'), 'r') as f:
|
|
||||||
expectation = f.read()
|
|
||||||
|
|
||||||
reality = str(blenderpack.extract_blinfo(os.path.join(self.helper_path, 'addons', 'add_curve_extra_objects/')))
|
|
||||||
self.assertEqual(expectation, reality)
|
|
||||||
|
|
||||||
def test_extract_blinfo_from_nonexistent(self):
|
def test_extract_blinfo_from_nonexistent(self):
|
||||||
|
test_file = 'file_that_doesnt_exist'
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
FileNotFoundError,
|
FileNotFoundError,
|
||||||
lambda: blenderpack.extract_blinfo(os.path.join(self.helper_path, 'addons', 'notathing'))
|
blenderpack.extract_blinfo,
|
||||||
|
os.path.join(self.addon_path, test_file)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_extract_blinfo_from_nonaddon(self):
|
||||||
|
test_file = 'not_an_addon.py'
|
||||||
|
self.assertRaises(
|
||||||
|
blenderpack.BadAddon,
|
||||||
|
blenderpack.extract_blinfo,
|
||||||
|
os.path.join(self.addon_path, test_file)
|
||||||
|
)
|
||||||
|
|
||||||
def test_make_repo_valid(self):
|
def test_make_repo_valid(self):
|
||||||
blenderpack.make_repo(os.path.join(self.helper_path, 'addons'))
|
blenderpack.make_repo(os.path.join(self.helper_path, 'addons'))
|
||||||
|
repojson = os.path.join(self.helper_path, 'addons', 'repo.json')
|
||||||
|
|
||||||
|
self.assertTrue(os.path.exists(repojson))
|
||||||
|
with open(repojson, 'r') as f:
|
||||||
|
json.loads(f.read())
|
||||||
|
|
||||||
|
os.remove(repojson)
|
||||||
|
self.fail('unfinished test')
|
||||||
|
|
||||||
def test_make_repo_from_nonexistent(self):
|
def test_make_repo_from_nonexistent(self):
|
||||||
blenderpack.make_repo(os.path.join(self.helper_path, 'addons'))
|
blenderpack.make_repo(os.path.join(self.helper_path, 'addons'))
|
||||||
|
self.fail('unfinished test')
|
||||||
|
|
||||||
|
|
||||||
|
# testname: filename
|
||||||
|
bl_info_tests = {
|
||||||
|
'test_extract_blinfo_from_file': 'real_addon.py',
|
||||||
|
'test_extract_blinfo_from_zip': 'zipped_addon.zip',
|
||||||
|
'test_extract_blinfo_from_dir': 'dir_addon',
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_test(test_file):
|
||||||
|
def test(self):
|
||||||
|
reality = str(blenderpack.extract_blinfo(os.path.join(self.addon_path, test_file)))
|
||||||
|
with open(os.path.join(self.helper_path, test_file + '_output'), 'r') as f:
|
||||||
|
expectation = f.read()
|
||||||
|
self.assertEqual(expectation, reality)
|
||||||
|
return test
|
||||||
|
|
||||||
|
for name, param in bl_info_tests.items():
|
||||||
|
test_func = generate_test(param)
|
||||||
|
setattr(test_blenderpack_make_repo, 'test_{}'.format(name), test_func)
|
||||||
|
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,261 +0,0 @@
|
|||||||
# 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()
|
|
@@ -1,284 +0,0 @@
|
|||||||
# 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()
|
|
@@ -1,491 +0,0 @@
|
|||||||
# 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
@@ -1,424 +0,0 @@
|
|||||||
# 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
@@ -1,726 +0,0 @@
|
|||||||
# ##### 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'}
|
|
@@ -1,398 +0,0 @@
|
|||||||
# 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()
|
|
@@ -1,422 +0,0 @@
|
|||||||
# ##### 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()
|
|
0
tests/test_helpers/addons/dir_addon/other.py
Normal file
0
tests/test_helpers/addons/dir_addon/other.py
Normal file
12
tests/test_helpers/addons/invalid_addon.py
Normal file
12
tests/test_helpers/addons/invalid_addon.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
bl_info = {
|
||||||
|
"author": "testscreenings, PKHG, TrumanBlending",
|
||||||
|
"version": (0, 1, 2),
|
||||||
|
"blender": (2, 59, 0),
|
||||||
|
"location": "View3D > Add > Curve",
|
||||||
|
"description": "Adds generated ivy to a mesh object starting "
|
||||||
|
"at the 3D cursor",
|
||||||
|
"warning": "",
|
||||||
|
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
|
||||||
|
"Scripts/Curve/Ivy_Gen",
|
||||||
|
"category": "Add Curve",
|
||||||
|
}
|
0
tests/test_helpers/addons/not_an_addon.py
Normal file
0
tests/test_helpers/addons/not_an_addon.py
Normal file
70
tests/test_helpers/addons/repo.json
Normal file
70
tests/test_helpers/addons/repo.json
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"bl_info": {
|
||||||
|
"author": "Multiple Authors",
|
||||||
|
"blender": [
|
||||||
|
2,
|
||||||
|
76,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"category": "Add Curve",
|
||||||
|
"description": "Add extra curve object types",
|
||||||
|
"location": "View3D > Add > Curve > Extra Objects",
|
||||||
|
"name": "Extra Objects",
|
||||||
|
"version": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"warning": "",
|
||||||
|
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Curve_Objects"
|
||||||
|
},
|
||||||
|
"type": "addon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bl_info": {
|
||||||
|
"author": "testscreenings, PKHG, TrumanBlending",
|
||||||
|
"blender": [
|
||||||
|
2,
|
||||||
|
59,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"category": "Add Curve",
|
||||||
|
"description": "Adds generated ivy to a mesh object starting at the 3D cursor",
|
||||||
|
"location": "View3D > Add > Curve",
|
||||||
|
"name": "IvyGen",
|
||||||
|
"version": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"warning": "",
|
||||||
|
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Ivy_Gen"
|
||||||
|
},
|
||||||
|
"type": "addon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bl_info": {
|
||||||
|
"author": "Multiple Authors",
|
||||||
|
"blender": [
|
||||||
|
2,
|
||||||
|
76,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"category": "Add Curve",
|
||||||
|
"description": "Add extra curve object types",
|
||||||
|
"location": "View3D > Add > Curve > Extra Objects",
|
||||||
|
"name": "Extra Objects",
|
||||||
|
"version": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"warning": "",
|
||||||
|
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Curve_Objects"
|
||||||
|
},
|
||||||
|
"type": "addon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
tests/test_helpers/addons/zipped_addon.zip
Normal file
BIN
tests/test_helpers/addons/zipped_addon.zip
Normal file
Binary file not shown.
1
tests/test_helpers/zipped_addon.zip_output
Normal file
1
tests/test_helpers/zipped_addon.zip_output
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{'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'}
|
Reference in New Issue
Block a user