Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'add_curve_extra_objects/add_curve_torus_knots.py')
-rw-r--r--add_curve_extra_objects/add_curve_torus_knots.py297
1 files changed, 156 insertions, 141 deletions
diff --git a/add_curve_extra_objects/add_curve_torus_knots.py b/add_curve_extra_objects/add_curve_torus_knots.py
index e6499de5..e093257a 100644
--- a/add_curve_extra_objects/add_curve_torus_knots.py
+++ b/add_curve_extra_objects/add_curve_torus_knots.py
@@ -16,7 +16,7 @@
#
# ##### END GPL LICENSE BLOCK #####
-'''
+"""
bl_info = {
"name": "Torus Knots",
"author": "Marius Giurgi (DolphinDream), testscreenings",
@@ -25,14 +25,11 @@ bl_info = {
"location": "View3D > Add > Curve",
"description": "Adds many types of (torus) knots",
"warning": "",
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
+ "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Curve/Torus_Knot",
"category": "Add Curve"}
-'''
+"""
-# ------------------------------------------------------------------------------
-
-#### import modules
import bpy
from bpy.props import (
BoolProperty,
@@ -41,18 +38,21 @@ from bpy.props import (
IntProperty
)
from math import (
- sin,
- cos,
- pi,
- sqrt
+ sin, cos,
+ pi, sqrt
+ )
+from mathutils import (
+ Vector,
+ Matrix,
)
-from mathutils import *
-from bpy_extras.object_utils import AddObjectHelper, object_data_add
+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:
@@ -61,9 +61,9 @@ def gcd(a, b):
return gcd(b, a % b)
-########################################################################
-####################### Knot Definitions ###############################
-########################################################################
+# #######################################################################
+# ###################### 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)
@@ -85,19 +85,19 @@ def Torus_Knot(self, linkIndex=0):
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
+ # 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)
+ # 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
+ # 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
+ # user defined phasing
if self.options_plus:
rPhase = self.torus_rP # user defined revolution phase
sPhase = self.torus_sP # user defined spin phase
@@ -113,35 +113,37 @@ def Torus_Knot(self, linkIndex=0):
print("gcd = %i" % links)
print("p = %i" % p)
print("q = %i" % q)
- print("link phase = %.2f deg" % (linkPhase * 180/pi))
+ print("link phase = %.2f deg" % (linkPhase * 180 / pi))
print("link phase = %.2f rad" % linkPhase)
-# flip directions ? NOTE: flipping both is equivalent to no flip
+ # 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
+ # 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
+ 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
+ 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
+ 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
+ # 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)))
@@ -161,8 +163,10 @@ def align_matrix(self, context):
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':
@@ -173,18 +177,20 @@ def setBezierHandles(obj, mode='AUTOMATIC'):
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)
+ # array for BEZIER spline output (V3)
if splineType == 'BEZIER':
for v in Verts:
vertArray += v
-# array for non-BEZIER output (V4)
+ # array for non-BEZIER output (V4)
else:
for v in Verts:
vertArray += v
@@ -195,20 +201,22 @@ def vertsToPoints(Verts, splineType):
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
+ # create curve
curve_data = bpy.data.curves.new(name=aName, type='CURVE')
-# setup materials to be used for the TK links
+ # setup materials to be used for the TK links
if self.use_colors:
addLinkColors(self, curve_data)
-# create torus knot link(s)
+ # create torus knot link(s)
if self.multiple_links:
links = gcd(self.torus_p, self.torus_q)
else:
@@ -218,34 +226,34 @@ def create_torus_knot(self, context):
# get vertices for the current link
verts = Torus_Knot(self, l)
-# output splineType 'POLY' 'NURBS' or 'BEZIER'
+ # output splineType 'POLY' 'NURBS' or 'BEZIER'
splineType = self.outputType
-# turn verts into proper array (based on spline type)
+ # turn verts into proper array (based on spline type)
vertArray = vertsToPoints(verts, splineType)
-# create spline from vertArray (based on spline type)
+ # 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.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.add(int(len(vertArray) * 1.0 / 4 - 1))
spline.points.foreach_set('co', vertArray)
spline.use_endpoint_u = True
-# set curve options
+ # set curve options
spline.use_cyclic_u = True
spline.order_u = 4
-# set a color per link
+ # 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 ?
+ # create surface ?
if self.geo_surface:
curve_data.fill_mode = 'FULL'
curve_data.bevel_depth = self.geo_bDepth
@@ -255,21 +263,23 @@ def create_torus_knot(self, context):
new_obj = bpy.data.objects.new(aName, curve_data)
-# set object in the scene
+ # 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
+ # 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 = []
@@ -295,13 +305,12 @@ def addLinkColors(self, curveData):
colors += [[1.0, 0.0, 0.0]]
me = curveData
- mat_offset = len(me.materials)
links = gcd(self.torus_p, self.torus_q)
- mats = []
+
for i in range(links):
matName = "TorusKnot-Link-%i" % i
matListNames = bpy.data.materials.keys()
-# create the material
+ # create the material
if matName not in matListNames:
if DEBUG:
print("Creating new material : %s" % matName)
@@ -311,7 +320,7 @@ def addLinkColors(self, curveData):
print("Material %s already exists" % matName)
mat = bpy.data.materials[matName]
-# set material color
+ # set material color
if self.options_plus and self.random_colors:
mat.diffuse_color = random(), random(), random()
else:
@@ -325,10 +334,11 @@ def addLinkColors(self, curveData):
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'}
@@ -341,10 +351,10 @@ class torus_knot_plus(Operator, AddObjectHelper):
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 for the invoke
align_matrix = None
-# GENERAL options
+ # GENERAL options
options_plus = BoolProperty(
name="Extra Options",
default=False,
@@ -355,8 +365,7 @@ class torus_knot_plus(Operator, AddObjectHelper):
default=False,
description="Set absolute location instead of relative to 3D cursor",
)
-
-# COLOR options
+ # COLOR options
use_colors = BoolProperty(
name="Use Colors",
default=False,
@@ -364,8 +373,8 @@ class torus_knot_plus(Operator, AddObjectHelper):
)
colorSet = EnumProperty(
name="Color Set",
- items=(('1', 'RGBish', 'RGBsish ordered colors'),
- ('2', 'Rainbow', 'Rainbow ordered colors')),
+ items=(('1', "RGBish", "RGBsish ordered colors"),
+ ('2', "Rainbow", "Rainbow ordered colors")),
)
random_colors = BoolProperty(
name="Randomize Colors",
@@ -378,8 +387,7 @@ class torus_knot_plus(Operator, AddObjectHelper):
min=0.0, max=1.0,
description="Color saturation",
)
-
-# SURFACE Options
+ # SURFACE Options
geo_surface = BoolProperty(
name="Surface",
default=True,
@@ -410,46 +418,45 @@ class torus_knot_plus(Operator, AddObjectHelper):
min=0, soft_min=0,
description="Offset the surface relative to the curve"
)
-
-# TORUS KNOT Options
+ # 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"
+ 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"
+ description="Number of Spins through the torus hole before closing the knot"
)
flip_p = BoolProperty(
name="Flip p",
default=False,
- description="Flip REVOLUTION direction"
+ description="Flip Revolution direction"
)
flip_q = BoolProperty(
name="Flip q",
default=False,
- description="Flip SPIN direction"
+ 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"
+ description="Generate all links or just one link when q and q are not co-primes"
)
torus_u = IntProperty(
- name="p multiplier",
+ name="Rev. Multiplier",
default=1,
min=1, soft_min=1,
- description="p multiplier"
+ description="Revolutions Multiplier"
)
torus_v = IntProperty(
- name="q multiplier",
+ name="Spin Multiplier",
default=1,
min=1, soft_min=1,
- description="q multiplier"
+ description="Spin multiplier"
)
torus_rP = FloatProperty(
name="Revolution Phase",
@@ -463,8 +470,7 @@ class torus_knot_plus(Operator, AddObjectHelper):
min=0.0, soft_min=0.0,
description="Phase spins by this radian amount"
)
-
-# TORUS DIMENSIONS options
+ # TORUS DIMENSIONS options
mode = EnumProperty(
name="Torus Dimensions",
items=(("MAJOR_MINOR", "Major/Minor",
@@ -517,8 +523,7 @@ class torus_knot_plus(Operator, AddObjectHelper):
min=0.0, max=100.0,
description="Scale along the local Z axis"
)
-
-# CURVE options
+ # CURVE options
torus_res = IntProperty(
name="Curve Resolution",
default=100,
@@ -532,9 +537,9 @@ class torus_knot_plus(Operator, AddObjectHelper):
description="Curve subdivisions per segment"
)
SplineTypes = [
- ('POLY', 'Poly', 'POLY'),
- ('NURBS', 'Nurbs', 'NURBS'),
- ('BEZIER', 'Bezier', 'BEZIER')]
+ ('POLY', "Poly", "Poly type"),
+ ('NURBS', "Nurbs", "Nurbs type"),
+ ('BEZIER', "Bezier", "Bezier type")]
outputType = EnumProperty(
name="Output splines",
default='BEZIER',
@@ -542,8 +547,8 @@ class torus_knot_plus(Operator, AddObjectHelper):
items=SplineTypes,
)
bezierHandles = [
- ('VECTOR', 'Vector', 'VECTOR'),
- ('AUTOMATIC', 'Auto', 'AUTOMATIC'),
+ ('VECTOR', "Vector", "Bezier Hanles type - Vector"),
+ ('AUTOMATIC', "Auto", "Bezier Hanles type - Automatic"),
]
handleType = EnumProperty(
name="Handle type",
@@ -557,122 +562,127 @@ class torus_knot_plus(Operator, AddObjectHelper):
description="Auto adjust curve resolution based on TK length",
)
-##### DRAW #####
def draw(self, context):
layout = self.layout
-# extra parameters toggle
- layout.prop(self, 'options_plus')
+ # extra parameters toggle
+ layout.prop(self, "options_plus")
-# TORUS KNOT Parameters
+ # TORUS KNOT Parameters
col = layout.column()
col.label(text="Torus Knot Parameters:")
+
box = layout.box()
- row = box.row()
- row.column().prop(self, 'torus_p')
- row.column().prop(self, 'flip_p')
- row = box.row()
- row.column().prop(self, 'torus_q')
- row.column().prop(self, 'flip_q')
+ 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()
- box.prop(self, 'torus_u')
- box.prop(self, 'torus_v')
- box.prop(self, 'torus_rP')
- box.prop(self, 'torus_sP')
+ 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
+ # 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':
+ if self.mode == "MAJOR_MINOR":
col = box.column(align=True)
col.prop(self, "torus_R")
-
- col = box.column(align=True)
col.prop(self, "torus_r")
else: # EXTERIOR-INTERIOR
col = box.column(align=True)
col.prop(self, "torus_eR")
-
- col = box.column(align=True)
col.prop(self, "torus_iR")
if self.options_plus:
box = box.box()
- box.prop(self, 'torus_s')
- box.prop(self, 'torus_h')
+ col = box.column(align=True)
+ col.prop(self, "torus_s")
+ col.prop(self, "torus_h")
-# CURVE options
+ # 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)
+ 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.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')
+ box.prop(self, "adaptive_resolution")
+ box.prop(self, "segment_res")
-# SURFACE options
+ # SURFACE options
col = layout.column()
col.label(text="Geometry Options:")
box = layout.box()
- box.prop(self, 'geo_surface')
+ box.prop(self, "geo_surface")
if self.geo_surface:
- box.prop(self, 'geo_bDepth')
- box.prop(self, 'geo_bRes')
- box.prop(self, 'geo_extrude')
- box.prop(self, 'geo_offset')
+ col = box.column(align=True)
+ col.prop(self, "geo_bDepth")
+ col.prop(self, "geo_bRes")
-# COLOR options
+ 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')
+ 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')
+ box.prop(self, "colorSet")
+ box.prop(self, "random_colors")
+ box.prop(self, "saturation")
-# TRANSFORM options
+ # 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')
+ box.prop(self, "location")
+ box.prop(self, "absolute_location")
+ box.prop(self, "rotation")
-##### POLL #####
@classmethod
def poll(cls, context):
if context.mode != "OBJECT":
return False
return context.scene is not None
-##### EXECUTE #####
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
+ 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
@@ -681,30 +691,35 @@ class torus_knot_plus(Operator, AddObjectHelper):
R = self.torus_R
r = self.torus_r
links = gcd(p, q)
-# get an approximate length of the whole TK curve
- maxTKLen = 2*pi*sqrt(p*p*(R+r)*(R+r) + q*q*r*r) # upper bound approximation
- minTKLen = 2*pi*sqrt(p*p*(R-r)*(R-r) + q*q*r*r) # lower bound approximation
- avgTKLen = (minTKLen + maxTKLen)/2 # average approximation
+
+ # 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)
- self.torus_res = max(3, avgTKLen/links * 8) # x N factor = control points per unit length
-# update align matrix
+ # 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
+ # 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 the curve
create_torus_knot(self, context)
-# restore pre operator undo state
+ # restore pre operator undo state
bpy.context.user_preferences.edit.use_global_undo = undo
return {'FINISHED'}
-##### INVOKE #####
def invoke(self, context, event):
self.execute(context)