#!BPY

"""
Name: 'Threaded_Pipe'
Blender: 248
Group: 'AddMesh'
"""

__author__= ['Luis Sergio S. Moura Jr.']
__url__ = ("http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Add/Threaded_Pipe")
__version__= '0.3'
__bpydoc__= '''\

Thread Mesh

0.5 - 2009-05-17 by Jeff Haran<br />
- modify to make it produce threaded pipes

0.4 - 2009-05-16 by Jeff Haran<br />
- redid algorythm that creates threads, add left or right handed thread selection

0.3 - 2007-07-29 by Luis Sergio<br />
- shoulder

0.2 - 2007-07-29 by Luis Sergio<br />
- renamed from bolt to thread
- bugfix: only 8-sided thread working
- edge crease option

0.1 - 2007-07-27 by Luis Sergio<br />
- initial version

'''

# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (c) Luis Sergio S. Moura Jr., 2007
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------


import BPyAddMesh
import Blender
from Blender.Window import EditMode
import bpy
import BPyMessages
import math

# Returns a 3d vector given angle, radius and z (optional)
def point(RIGHTHANDED, ANGLE, RADIUS, DZ = 0):
	if (RIGHTHANDED):
		vert = [RADIUS * math.sin(-ANGLE), RADIUS * math.cos(-ANGLE), DZ]
	else:
		vert = [RADIUS * math.sin(ANGLE), RADIUS * math.cos(ANGLE), DZ]
	return vert

def make_face(V0, V1, V2, V3, RIGHTHANDED):
	if (RIGHTHANDED == 0):
		face = [V0, V1, V2, V3]
	else:
		face = [V0, V3, V2, V1]
	return face

def make_face_3(V0, V1, V2, RIGHTHANDED):
	if (RIGHTHANDED == 0):
		face = [V0, V1, V2]
	else:
		face = [V0, V2, V1]
	return face

# actually creates the vertices and the faces for the bolt.
def create_bolt(RADIUS, SMOOTHRADIUS, SUBDIVISIONS, STEP, LOOPS, IDENT, SHOULDER, RIGHTHANDED):
	# list of all vertices
	verts = []
	# list of all faces
	faces = []

	# list of verts[] indices that constitute beginning of threads
	bottomcap = []
	# list of verts[] indices that constitute threads end of smooth cylinder
	bottomcapsmooth = []
	# list of verts[] indices that constitute end of threads
	topcap = []
	# list of verts[] indices that constitute outside of shoulder end
	shouldercap = []
	shouldercap180 = []
	# list of verts[] indices that constitute shoulder end of smooth cylinder
	shouldercapsmooth = []

	# list of verts[] indices that constitute outside edge of thread
	outthreads = []
	# list of verts[] indices that constitute inside edge of thread
	inthreads = []

	# ========= VERTS =========

	# 1/2 turn of two intermixed sequences of verts starting 180 deg apart
	# defining intersection of thread and end cut
	for i in range(SUBDIVISIONS):
		deg = math.pi * i / SUBDIVISIONS
		ident = IDENT * (1 - ((1.0 * i) / SUBDIVISIONS))
		bottomcap.append(len(verts))
		outthreads.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS - ident))
		deg = math.pi * i / SUBDIVISIONS + math.pi
		ident = IDENT * ((1.0 * i)/SUBDIVISIONS)
		bottomcap.append(len(verts))
		inthreads.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS - ident))

	# next 1/2 turn, two intermixed sequences of verts
	# one defining top of thread, other defining bottom
	for i in range(SUBDIVISIONS, 2 * SUBDIVISIONS):
		deg = math.pi * i / SUBDIVISIONS
		outthreads.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS, ((i - SUBDIVISIONS) * STEP / 2 / SUBDIVISIONS))) 
		deg = math.pi * i / SUBDIVISIONS + math.pi
		inthreads.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS - IDENT, ((i - SUBDIVISIONS) * STEP / 2 / SUBDIVISIONS))) 

	lastdisp = STEP / 2

	# LOOPS - 1 more complete turns
	for j in range(1, LOOPS):
		for i in range(SUBDIVISIONS * 2):
			deg = math.pi * i / SUBDIVISIONS
			outthreads.append(len(verts))
			verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp + (i * STEP / 2 / SUBDIVISIONS))) 
			deg = math.pi * i / SUBDIVISIONS + math.pi
			inthreads.append(len(verts))
			verts.append(point(RIGHTHANDED, deg, RADIUS - IDENT, lastdisp + (i * STEP / 2 / SUBDIVISIONS))) 
		lastdisp = lastdisp + STEP


	# 1 turn of thread cut fading away
	for i in range(SUBDIVISIONS * 2):
		deg = math.pi * i / SUBDIVISIONS
		ident = IDENT * (1 - ((1.0 * i) / (SUBDIVISIONS * 2)))
		outthreads.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp + (i * STEP / 2 / SUBDIVISIONS))) 
		deg = math.pi * i / SUBDIVISIONS + math.pi
		inthreads.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS - ident, lastdisp + (i * STEP / 2 / SUBDIVISIONS))) 
	lastdisp = lastdisp + STEP

	# 1 turn with no thread cut
	for i in range(SUBDIVISIONS * 2):
		deg = math.pi * i / SUBDIVISIONS
		verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp + (i * STEP / 2 / SUBDIVISIONS))) 
		deg = math.pi * i / SUBDIVISIONS + math.pi
		verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp + (i * STEP / 2 / SUBDIVISIONS))) 
	lastdisp = lastdisp + STEP

	# 1/2 turn to end the threads
	for i in range(SUBDIVISIONS):
		deg = math.pi * i / SUBDIVISIONS
		topcap.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp))
		deg = math.pi * i / SUBDIVISIONS + math.pi
		topcap.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp))

	if (SHOULDER != 0):
		# 1/2 turn to make the shoulder
		for i in range(SUBDIVISIONS, 2 * SUBDIVISIONS):
			deg = math.pi * i / SUBDIVISIONS
			shouldercap180.append(len(verts))
			verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp + SHOULDER))
			deg = math.pi * i / SUBDIVISIONS + math.pi
			shouldercap180.append(len(verts))
			verts.append(point(RIGHTHANDED, deg, RADIUS, lastdisp + SHOULDER))
		lastdisp = lastdisp + SHOULDER
		# convert shouldercap180 sequence starting at 180 degrees to
		# shouldercap sequence starting at 0 degrees
		for i in range(0, SUBDIVISIONS * 2, 2):
			shouldercap.append(shouldercap180[i + 1])
			shouldercap.append(shouldercap180[i])
	else:
		shouldercap = topcap

	# circle of vertices defining cylinder at thread end
	for i in range(SUBDIVISIONS):
		deg = math.pi * i / SUBDIVISIONS
		bottomcapsmooth.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, SMOOTHRADIUS))
		deg = math.pi * i / SUBDIVISIONS + math.pi
		bottomcapsmooth.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, SMOOTHRADIUS))

	# circle of vertices defining cylinder at shoulder end
	for i in range(SUBDIVISIONS):
		deg = math.pi * i / SUBDIVISIONS
		shouldercapsmooth.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, SMOOTHRADIUS, lastdisp))
		deg = math.pi * i / SUBDIVISIONS + math.pi
		shouldercapsmooth.append(len(verts))
		verts.append(point(RIGHTHANDED, deg, SMOOTHRADIUS, lastdisp))

	# ========FACES ========

	# two intermixed sequences of faces starting from end cut
	# define the two sides of the thread

	threadfaces = SUBDIVISIONS * 4 * (LOOPS + 2) - 2
	for i in range(0, threadfaces, 2):
		faces.append(make_face(i, i + (SUBDIVISIONS * 2) + 1, i + (SUBDIVISIONS * 2) + 3, i + 2, RIGHTHANDED))
		faces.append(make_face(i + 1, i + (SUBDIVISIONS * 2), i + (SUBDIVISIONS * 2) + 2, i + 3, RIGHTHANDED))

	# last pair of thread faces are triangles
	faces.append(make_face_3(threadfaces, threadfaces + (SUBDIVISIONS * 2) + 1, threadfaces + 2, RIGHTHANDED))
	faces.append(make_face_3(threadfaces + 1, threadfaces + (SUBDIVISIONS * 2), threadfaces + 3, RIGHTHANDED))
	threadfaces = threadfaces + 2

	if (SHOULDER != 0):
		for i in range(threadfaces, threadfaces + (SUBDIVISIONS * 2) - 2, 2):
			faces.append(make_face(i, i + (SUBDIVISIONS * 2) + 1, i + (SUBDIVISIONS * 2) + 3, i + 2, RIGHTHANDED))
			faces.append(make_face(i + 1, i + (SUBDIVISIONS * 2), i + (SUBDIVISIONS * 2) + 2, i + 3, RIGHTHANDED))
		i = threadfaces + (SUBDIVISIONS * 2) - 2
		faces.append(make_face(i, i + (SUBDIVISIONS * 2) + 1, i + 2, i + 3 - (SUBDIVISIONS * 2), RIGHTHANDED))
		faces.append(make_face(i + 1, i + (SUBDIVISIONS * 2), i + 3 , i + 3 - (SUBDIVISIONS * 2) - 1, RIGHTHANDED))

	# faces on thread end
	for i in range(SUBDIVISIONS * 2 - 2):
		faces.append(make_face(bottomcap[i], bottomcap[i + 2], bottomcapsmooth[i + 2], bottomcapsmooth[i], RIGHTHANDED))
	i = SUBDIVISIONS * 2 - 2
	faces.append(make_face(bottomcap[i], bottomcap[1], bottomcapsmooth[1], bottomcapsmooth[i], RIGHTHANDED))
	i = i + 1
	faces.append(make_face(bottomcap[i], bottomcap[0], bottomcapsmooth[0], bottomcapsmooth[i], RIGHTHANDED))

	# faces on shoulder end
	for i in range(SUBDIVISIONS * 2 - 2):
		faces.append(make_face(shouldercap[i], shouldercapsmooth[i], shouldercapsmooth[i + 2], shouldercap[i + 2], RIGHTHANDED))
	i = SUBDIVISIONS * 2 - 2
	faces.append(make_face(shouldercap[i], shouldercapsmooth[i], shouldercapsmooth[1], shouldercap[1], RIGHTHANDED))
	i = i + 1
	faces.append(make_face(shouldercap[i], shouldercapsmooth[i], shouldercapsmooth[0], shouldercap[0], RIGHTHANDED))

	# faces composing smooth cylinder
	for i in range(SUBDIVISIONS * 2 - 2):
		faces.append(make_face(bottomcapsmooth[i], bottomcapsmooth[i + 2], shouldercapsmooth[i + 2], shouldercapsmooth[i], RIGHTHANDED))

	i = SUBDIVISIONS * 2 - 2
	faces.append(make_face(bottomcapsmooth[i], bottomcapsmooth[1], shouldercapsmooth[1], shouldercapsmooth[i], RIGHTHANDED))
	i = i + 1
	faces.append(make_face(bottomcapsmooth[i], bottomcapsmooth[0], shouldercapsmooth[0], shouldercapsmooth[i], RIGHTHANDED))

	return verts, faces, outthreads, inthreads, bottomcap, bottomcapsmooth, shouldercap, shouldercapsmooth

# Check if vertice is an outer edge.
def is_member(OFFSET, EDGE, VERTS):
	v1 = EDGE.v1.index - OFFSET
	v2 = EDGE.v2.index - OFFSET
	if ((v1 < 0) or (v2 < 0)):
		return False

	v1_found = False
	v2_found = False

        for i in range(len(VERTS)):
		if (v1 == VERTS[i]):
			v1_found = True
		if (v2 == VERTS[i]):
			v2_found = True

	return v1_found and v2_found

# main function (window handle and input variables)
def main():
	verts = []
	faces = []		
	outthreads = []
	inthreads = []
	bottomcap = []
	bottomcapsmooth = []
	shouldercap = []
	shouldercapsmooth = []

	boltRadiusInput = Blender.Draw.Create(1.0)
	boltSmoothRadiusInput = Blender.Draw.Create(0.8)
	boltDivInput = Blender.Draw.Create(8)
	boltStepInput = Blender.Draw.Create(.2)
	boltLoopsInput = Blender.Draw.Create(5)
	boltIdentInput = Blender.Draw.Create(.1)
	boltShoulderInput = Blender.Draw.Create(0.000)
	boltRightHandedInput = Blender.Draw.Create(1)

	boltCreaseInsideInput = Blender.Draw.Create(0.000);
	boltCreaseOutsideInput = Blender.Draw.Create(0.000);
	boltCreaseCapInput = Blender.Draw.Create(0.000);
	
	block = []
	block.append(("Radius:", boltRadiusInput, 0.01, 1000, "the radius"))
	block.append(("Smooth Radius:", boltSmoothRadiusInput, 0.01, 1000, "smooth radius"))
	block.append(("Divisions:", boltDivInput, 4, 100, "the bolt divisions"))
	block.append(("Step:", boltStepInput, -100, 100, "step length"))
	block.append(("Loops:", boltLoopsInput, 1, 1000, "the height of the pipe"))
	block.append(("Ident:", boltIdentInput, -100, 100, "number of height divisions"))
	block.append(("Shoulder:", boltShoulderInput, -1000, 1000, "height of the shoulder"))
	block.append(("Right Handled:", boltRightHandedInput, 0, 1, "1 right, 0 left"))
	block.append("Edge Creases");
	block.append(("Inside:", boltCreaseInsideInput, .00, 1.000, "inside crease"))
	block.append(("Outside:", boltCreaseOutsideInput, .00, 1.000, "outside crease"))
	block.append(("Cap:", boltCreaseCapInput, .00, 1.000, "cap crease"))
	
	if not Blender.Draw.PupBlock("Create thread", block):
		return

#	Generates the mesh
	verts, faces, outthreads, inthreads, bottomcap, bottomcapsmooth, shouldercap, shouldercapsmooth = create_bolt(boltRadiusInput.val,boltSmoothRadiusInput.val,boltDivInput.val,boltStepInput.val,boltLoopsInput.val,boltIdentInput.val, boltShoulderInput.val,boltRightHandedInput.val)

	# Edge crease stuff
	# Most of this is "imported" from BPyAddMesh.py module from blender 2.44
	# We need to get the number of vertices of the existing object, if any.
	scn = bpy.data.scenes.active
	if scn.lib: return
	ob_act = scn.objects.active

	vert_offset = 0
	edge_offset = 0
	if EditMode():
		me = ob_act.getData(mesh=1)
		if me.multires:
			BPyMessages.Error_NoMeshMultiresEdit()
			return
		EditMode(0)
		
		me.sel = False
		
		vert_offset = len(me.verts)
		edge_offset = len(me.edges)
		# we dont need this
		# face_offset = len(me.faces)

	# ---


	# Adds the mesh to the scene
	BPyAddMesh.add_mesh_simple('Thread_Pipe', verts, [], faces)

	# Set the correct edge creases
	EditMode(0)
	SUBDIVISIONS = boltDivInput.val
	ob_act = scn.objects.active
	me = ob_act.getData(mesh=1)
	me.sel = False

	vcount = len(me.verts) - vert_offset
	INNER_CREASE = int(255 * boltCreaseInsideInput.val)
	OUTER_CREASE = int(255 * boltCreaseOutsideInput.val)
	CAP_CREASE = int(255 * boltCreaseCapInput.val)

	for ed in me.edges:
		# inner edge
		if (INNER_CREASE > 0):
			if (is_member(vert_offset, ed, inthreads)):
				ed.crease = INNER_CREASE

		# outer edge
		if (OUTER_CREASE > 0):
			if (is_member(vert_offset, ed, outthreads)):
				ed.crease = OUTER_CREASE

		# cap edge
		if (CAP_CREASE > 0):
			if (is_member(vert_offset, ed, bottomcap)):
				ed.crease = CAP_CREASE
			if (is_member(vert_offset, ed, shouldercap)):
				ed.crease = CAP_CREASE
			if (is_member(vert_offset, ed, bottomcapsmooth)):
				ed.crease = CAP_CREASE
			if (is_member(vert_offset, ed, shouldercapsmooth)):
				ed.crease = CAP_CREASE


	EditMode(0)
		
	
	


# call our main function	
main()
