# GPL #  Author, Anthony D'Agostino

import bpy, mathutils, math

def create_mesh_object(context, verts, edges, faces, name):
    # Create new mesh
    mesh = bpy.data.meshes.new(name)
    # Make a mesh from a list of verts/edges/faces.
    mesh.from_pydata(verts, edges, faces)
    # Update mesh geometry after adding stuff.
    mesh.update()
    from bpy_extras import object_utils
    return object_utils.object_data_add(context, mesh, operator=None)

# ========================
# === Torus Knot Block ===
# ========================
def k1(t):
	x = math.cos(t) - 2*math.cos(2*t)
	y = math.sin(t) + 2*math.sin(2*t)
	z = math.sin(3*t)
	return mathutils.Vector([x,y,z])

def k2(t):
	x = 10 * (math.cos(t) + math.cos(3*t)) + math.cos(2*t) + math.cos(4*t)
	y = 6 * math.sin(t) + 10 * math.sin(3*t)
	z = 4 * math.sin(3*t) * math.sin(5*t/2) + 4*math.sin(4*t) - 2*math.sin(6*t)
	return mathutils.Vector([x,y,z]) * 0.2

def k3(t):
	x = 2.5*math.cos(t+math.pi)/3 + 2*math.cos(3*t)
	y = 2.5*math.sin(t)/3 + 2*math.sin(3*t)
	z = 1.5*math.sin(4*t) + math.sin(2*t)/3
	return mathutils.Vector([x,y,z])

def make_verts(ures, vres, r2, knotfunc):
	verts = []
	for i in range(ures):
		t1 = (i+0) * 2*math.pi/ures
		t2 = (i+1) * 2*math.pi/ures
		a = knotfunc(t1)		# curr point
		b = knotfunc(t2)		# next point
		a,b = map(mathutils.Vector, (a,b))
		e = a-b
		f = a+b
		g = e.cross(f)
		h = e.cross(g)
		g.normalize()
		h.normalize()
		for j in range(vres):
			k = j * 2*math.pi/vres
			l = (math.cos(k),0.0,math.sin(k))
			l = mathutils.Vector(l)
			m = l * r2
			x,y,z = m
			n = h*x
			o = g*z
			p = n+o
			q = a+p
			verts.append(q)
	return verts

def make_faces(ures, vres):
	faces = []
	for u in range(0, ures):
		for v in range(0, vres):
			p1 = v + u*vres
			p2 = v + ((u+1)%ures)*vres
			p4 = (v+1)%vres + u*vres
			p3 = (v+1)%vres + ((u+1)%ures)*vres
			faces.append([p4, p3, p2, p1])
	return faces

def make_knot(knotidx, ures):
	knots = [k1,k2,k3]
	knotfunc = knots[knotidx-1]
	vres = ures//10
	r2 = 0.5
	verts = make_verts(ures, vres, r2, knotfunc)
	faces = make_faces(ures, vres)
	return (verts, faces)

class AddTorusKnot(bpy.types.Operator):
	"""Add a torus-knot mesh"""
	bl_idname = "mesh.primitive_torusknot_add"
	bl_label = "Add Torus Knot"
	bl_options = {"REGISTER", "UNDO"}

	resolution = bpy.props.IntProperty(name="Resolution",
		description="Resolution of the Torus Knot",
		default=80, min=30, max=256)

	objecttype = bpy.props.IntProperty(name="Knot Type",
		description="Type of Knot",
		default=1, min=1, max=3)

	def execute(self, context):
		verts, faces = make_knot(self.objecttype,
								 self.resolution)
		obj = create_mesh_object(context, verts, [], faces, "Torus Knot")
		return {'FINISHED'}
