fn rotateVector v1 v2 amount=
(
	x=v1[1]
	y=v1[2]
	z=v1[3]
	u=v2[1]
	v=v2[2]
	w=v2[3]
	a=amount
	
	ux=u*x
	uy=u*y
	uz=u*z
	vx=v*x
	vy=v*y
	vz=v*z
	wx=w*x
	wy=w*y
	wz=w*z
	sa=Sin(a)
	ca=Cos(a)
	x=u*(ux+vy+wz)+(x*(v*v+w*w)-u*(vy+wz))*ca+(-wy+vz)*sa
	y=v*(ux+vy+wz)+(y*(u*u+w*w)-v*(ux+wz))*ca+(wx-uz)*sa
	z=w*(ux+vy+wz)+(z*(u*u+v*v)-w*(ux+vy))*ca+(-vx+uy)*sa
	
	result=normalize[x,y,z]
	
	result
)

plugin simpleobject SpiroTube
name:"SpiroTube"
classID:#(0x7967d905, 0x24780e59)
category:"Cheese3000 Primitives"
(
	parameters params rollout:params
	(
		radius type:#worldunits ui:spn1 default:5
		segments type:#integer ui:spn2 default:20
		sides type:#integer ui:spn3 default:10
		xPos type:#string ui:x_pos default:"m*cos(360.0*i/segments)"
		yPos type:#string ui:y_pos default:"m*sin(360.0*i/segments)"
		zPos type:#string ui:z_pos default:"0"
		obj type:#node ui:obj
		useObj type:#boolean ui:use animatable:false
	)
	parameters customValues rollout:customValues
	(
		m type:#float ui:spnM default:30
		n type:#float ui:spnn default:0
		p type:#float ui:spnp default:18
		q type:#float ui:spnq default:0
		h type:#float ui:spnh default:0
		t type:#float ui:spnt default:0
		presets_DO_NOT_CHANGE type:#integer ui:preset animatable:false
		on presets_DO_NOT_CHANGE set newSel do
		(
			case newSel of
			(
				1:(
					xPos="m*cos(360.0*i/segments)"
					yPos="m*sin(360.0*i/segments)"
					zPos="h"
					m=30
					h=0
				)
				2:(
					xPos="m*cos(p*i)+n*cos(q*i)"
					yPos="m*sin(p*i)+n*sin(q*i)"
					zPos="h*sin(t*i)"
					m=30
					n=6
					p=2
					q=-5
					h=2.5
					t=7
				)
				3:(
					xPos="m*cos(p*i)+n*cos(q*i)"
					yPos="m*sin(p*i)+n*sin(q*i)"
					zPos="h*sin(t*i)"
					m=15
					n=20
					p=1
					q=3
					h=4.5
					t=4
				)
				4:(
					xPos="m*cos(p*i)+n*cos(q*i)"
					yPos="m*sin(p*i)+n*sin(q*i)"
					zPos="h*sin(t*i)"
					m=15
					n=18.75
					p=3
					q=5
					h=2.5
					t=8
				)
				5:(
					xPos="m*cos(p*i)+n*cos(q*i)"
					yPos="m*sin(p*i)+n*sin(q*i)"
					zPos="h*sin(t*i)"
					m=20
					n=15
					p=2
					q=5
					h=2
					t=6
				)
				6:(
					xPos="m*cos(p*i)-n*cos(q*i)"
					yPos="m*sin(p*i)-n*sin(q*i)"
					zPos="h*sin(t*i)-0.3*sin(6.0*i)"
					m=15
					n=30
					p=1
					q=-3
					h=3
					t=4
				)
				7:(
					xPos="17.7*cos(1.0*i)+m*cos(p*i)-n*cos(q*i)"
					yPos="17.7*sin(1.0*i)+m*sin(p*i)-n*sin(q*i)"
					zPos="h*sin(t*i)+2.0*sin(6.0*i)"
					m=0.3*30
					n=0.45*30
					p=-2
					q=-5
					h=2
					t=9
				)
			)
		)
	)
	parameters tubeparams rollout:tubeParams
	(
		capEnd type:#boolean ui:end default:true animatable:false
		alignEnd type:#boolean ui:endAlign default:true animatable:false
		type type:#integer ui:radType default:1
		amount type:#worldunits ui:a1 default:0
		frequency type:#float ui:f1 default:4
		offset type:#float ui:o1 default:0
		custom type:#string ui:c1
		
		rotationAmount type:#float ui:r default:0
		twistAmount type:#float ui:t default:0
		twistBias type:#float ui:b default:0
		smoothType type:#integer ui:smoothType default:1
	)
	rollout params "Parameters"
	(
		fn pickfilter obj = superClassOf obj == shape
		spinner spn1 "Radius" type:#worldunits range:[0,999,5]
		spinner spn2 "Segments" type:#integer range:[3,9999,10]
		spinner spn3 "Sides" type:#integer range:[3,9999,10]
		edittext x_pos "X" pos:[8,88] width:152 height:17 text:"20*cos(360*i/segments)"
		edittext y_pos "Y" pos:[8,112] width:152 height:17 text:"20*sin(360*i/segments)"
		edittext z_pos "Z" pos:[8,136] width:152 height:17 text:"0"
		
		pickButton obj "Spline" pos:[8,158] width:88 height:26 autodisplay:true filter:pickfilter enabled:false
		checkbox use "Use" pos:[104,164] width:43 height:16
		button genSpline "Generate Spline"
		on genSpline pressed do
		(
			global i=1
			spline1=splineshape()
			spline1.name=uniquename "Spline curve"
			addnewspline spline1
			for j=1 to segments do
			(
				curPos=[0,0,0]
				pos1=execute x_pos.text
				pos2=execute y_pos.text
				pos3=execute z_pos.text
				if (isKindOf pos1 number)==true do
				(
					curPos[1]=pos1
				)
				if (isKindOf pos2 number)==true do
				(
					curPos[2]=pos2
				)
				if (isKindOf pos3 number)==true do
				(
					curPos[3]=pos3
				)
				addKnot spline1 1 #smooth #curve curPos
				i+=1
			)
			updateShape spline1
			select spline1
		)
		on obj picked newObj do
		(
			convertToSplineShape newObj
			use.state=true
		)
	)
	rollout customValues "Variables"
	(
		spinner spnM "M: " width:128 height:16 align:#right type:#float range:[-9999,9999,0]
		spinner spnN "N: " width:128 height:16 align:#right type:#float range:[-9999,9999,0]
		spinner spnP "P: " width:128 height:16 align:#right type:#float range:[-9999,9999,0]
		spinner spnQ "Q: " width:128 height:16 align:#right type:#float range:[-9999,9999,0]
		spinner spnH "H: " width:128 height:16 align:#right type:#float range:[-9999,9999,0]
		spinner spnT "T: " width:128 height:16 align:#right type:#float range:[-9999,9999,0]
		dropDownList preset "Preset" items:#("Torus","Torus_1","Figure Eight Knot","Figure Eight Knot_1","Inner circulating","Granny/square knot","Triple knot")
	)
	rollout tubeParams "Tube Parameters"
	(
		checkbox End "Connect end" pos:[8,8] width:85 height:15
		checkbox endAlign "Align" pos:[96,8] width:56 height:16
		on end changed state do
		(
			if state==false then
			(
				endAlign.enabled=true
			)
			else
			(
				endAlign.state=true
				endAlign.enabled=false
			)
		)
		radiobuttons radType "Radius" labels:#("Standard", "Custom") columns:1 offsets:#([0,0], [0,72]) align:#left
		spinner a1 "Amount: " pos:[32,64] width:120 height:16 enabled:true range:[-999,999,2.5] type:#worldunits
		spinner f1 "Frequency: " pos:[32,88] width:120 height:16 enabled:true range:[0,999,4] type:#float
		spinner o1 "Offset: " pos:[32,111] width:120 height:16 enabled:true type:#float range:[-9999,9999,0]
		edittext c1 "" pos:[16,152] width:136 height:17 enabled:false
		spinner R "Rotation: " width:120 height:16 align:#right type:#float range:[-360,360,0]
		spinner T "Twist: " width:120 height:16 align:#right type:#float range:[-9999,9999,0]
		spinner B "Bias: " width:120 height:16 align:#right type:#float range:[0,9999,0]
		radioButtons smoothType "Smoothing" labels:#("All","Segments","Sides","None") columns:1 align:#left
		on tubeParams reload do
		(
			if radType.state==1 then
			(
				c1.enabled=false
				a1.enabled=true
				f1.enabled=true
				o1.enabled=true
			)
			else
			(
				c1.enabled=true
				a1.enabled=false
				f1.enabled=false
				o1.enabled=false
			)
		)
		on radType changed state do
		(
			if state==1 then
			(
				c1.enabled=false
				a1.enabled=true
				f1.enabled=true
				o1.enabled=true
			)
			else
			(
				c1.enabled=true
				a1.enabled=false
				f1.enabled=false
				o1.enabled=false
			)
		)
	)
	on buildmesh do
	(
		--Making sure the strings are valid to use
		failed=true
		global i=0
		global segments=segments
		
		global m=m
		global n=n
		global p=p
		global q=q
		global h=h
		global t=t
		
		seg=segments
		try (execute xPos) catch (failed=false)
		if failed==true then
		(
			string1=xPos
		)
		else
		(
			string1="20*cos(360*i/segments)"
		)
		
		failed=true
		try (execute yPos) catch (failed=false)
		if failed==true then
		(
			string2=yPos
		)
		else
		(
			string2="20*sin(360*i/segments)"
		)
		
		failed=true
		try (execute zPos) catch (failed=false)
		if failed==true then
		(
			string3=zPos
		)
		else
		(
			string3="0"
		)
		
		verts=#( )
		pos=#( )
		--Getting initial positions
		useSpline=false
		if useObj==true do
		(
			if obj !=undefined do
			(
				useSpline=true
			)
		)
		if useSpline==true then
		(
			seg=numKnots obj 1
			segments=seg
			for i=1 to seg do
			(
				newPos=getKnotPoint obj 1 i
				pos[pos.count+1]=newPos
			)
		)
		else
		(
			for j=1 to segments do
			(
				curPos=[0,0,0]
				pos1=execute string1
				pos2=execute string2
				pos3=execute string3
				if (isKindOf pos1 number)==true do
				(
					curPos[1]=pos1
				)
				if (isKindOf pos2 number)==true do
				(
					curPos[2]=pos2
				)
				if (isKindOf pos3 number)==true do
				(
					curPos[3]=pos3
				)
				pos[pos.count+1]=curPos
				i+=1
			)
		)
		vectors=#( )
		vectors[1]=[0,0,0]
		--print pos
		--Finding vectors
		for i=2 to segments-1 do
		(
			newPos=pos[i]
			oldPos=pos[i-1]
			futurePos=pos[i+1]
			vector1=newPos-oldPos
			vector2=futurePos-newPos
			vector3=(vector1+vector2)/2
			vectors[i]=normalize(vector3)
		)
		if alignEnd==true then
		(
			curPos=pos[1]
			nextPos=pos[2]
			previousPos=pos[pos.count]
			vector1=nextPos-curPos
			vector2=curPos-previousPos
			vector3=(vector1+vector2)/2
			vectors[1]=normalize vector3
			
			curPos=pos[pos.count]
			nextPos=pos[1]
			previousPos=pos[pos.count-1]
			vector1=nextPos-curPos
			vector2=curPos-previousPos
			vector3=(vector1+vector2)/2
			vectors[vectors.count+1]=normalize vector3
		)
		else
		(
			curPos=pos[1]
			nextPos=pos[2]
			vector1=nextPos-curPos
			vectors[1]=normalize vector1
			
			curPos=pos[pos.count]
			nextPos=pos[pos.count-1]
			vector1=curPos-nextPos
			vectors[vectors.count+1]=normalize vector1
		)
		rotateAmount=(360.0/sides)
		global i=1
		for k=1 to segments do
		(
			newPos=pos[k]
			sVector=cross [0,0,1] vectors[k]
			bias=0
			for j=1 to sides do
			(
				if twistBias >=0 then
				(
					bias=((k as float)/(segments as float))^twistBias
					rot1=rotationAmount+(((twistamount*360)/segments)*i)*bias
				)
				else
				(
					newBias=twistBias*-1
					--bias=1.0-((((k-1.0) as float)/(segments as float))^newBias)
					bias=(1.0-((k as float)/(segments as float)))^newBias
					rot1=rotationAmount+(((twistamount*360)/segments)*i)*(bias*-1)
				)
				newV=rotateVector sVector vectors[k] ((rotateAmount*j)+rot1)
				newRadius=radius
				if type==1 then
				(
					newRadius=radius+(amount*cos((((frequency*360.0)/segments)*(k as float))+offset))
				)
				else
				(
					failed=false
					try (execute custom) catch (failed=true)
					if failed==false do
					(
						test=execute custom
						if (iskindof test number) do
						(
							newRadius=test+radius
							--print i
						)
					)
				)
				verts[verts.count+1]=newPos+(newV*newRadius)
			)
			--print bias
			i+=1
		)
		faces=#( )
		curNum=1
		tempNum=0
		--Faces...
		for i=1 to segments-1 do
		(
			tempNum=0
			for j=1 to sides-1 do
			(
				verts1=[curNum,curNum+1,curNum+sides]+tempNum
				verts2=[curNum+1,curNum+sides+1,curNum+sides]+tempNum
				faces[faces.count+1]=verts1
				faces[faces.count+1]=verts2
				tempNum+=1
			)
			verts1=[curNum+sides,curNum+(sides*2)-1,curNum]
			verts2=[curNum+sides-1,curNum,curNum+(sides*2)-1]
			faces[faces.count+1]=verts1
			faces[faces.count+1]=verts2
			curNum+=sides
		)
		--end faces...
		if capEnd==true do
		(
			tempNum=0
			num=(sides*seg)-(sides-1)
			for i=1 to sides-1 do
			(
				verts1=[1,num,num+1]+tempNum
				verts2=[2,1,num+1]+tempNum
				faces[faces.count+1]=verts1
				faces[faces.count+1]=verts2
				tempNum+=1
			)
			verts1=[1,sides,num]
			verts2=[(sides*segments),num,sides]
			faces[faces.count+1]=verts1
			faces[faces.count+1]=verts2
		)
		
		setmesh mesh verts:verts faces:faces
		
		num=1
		for i=1 to segments-1 do
		(
			for i=1 to sides-1 do
			(
				setEdgeVis mesh num 2 false
				setEdgeVis mesh (num+1) 3 false
				num+=2
			)
			setEdgeVis mesh num 2 false
			setEdgeVis mesh (num+1) 2 false
			num+=2
		)
		if capEnd==true do
		(
			for i=1 to sides-1 do
			(
				setEdgeVis mesh num 3 false
				setEdgeVis mesh (num+1) 2 false
				num+=2
			)
			setEdgeVis mesh num 2 false
			setEdgeVis mesh (num+1) 2 false
		)
		case smoothType of
		(
			1:(
				num=meshop.getNumFaces mesh
				for i=1 to num do
				(
					setFaceSmoothGroup mesh i 1
				)
			)
			2:(
				num=1
				smoothNum=1
				for i=1 to segments-1 do
				(
					for i=1 to sides-1 do
					(
						setFaceSmoothGroup mesh num smoothNum
						setFaceSmoothGroup mesh (num+1) smoothNum
						num+=2
					)
					setFaceSmoothGroup mesh num smoothNum
					setFaceSmoothGroup mesh (num+1) smoothNum
					num+=2
					smoothNum+=1
					if smoothNum==3 do
					(
						smoothNum=1
					)
				)
				if capEnd==true do
				(
					for i=1 to sides-1 do
					(
						setFaceSmoothGroup mesh num smoothNum
						setFaceSmoothGroup mesh (num+1) smoothNum
						num+=2
					)
					setFaceSmoothGroup mesh num smoothNum
					setFaceSmoothGroup mesh (num+1) smoothNum
				)
			)
			3:(
				num=1
				smoothNum=0
				for i=1 to segments-1 do
				(
					for i=1 to sides-1 do
					(
						smoothNum+=1
						if smoothNum==3 do
						(
							smoothNum=1
						)
						setFaceSmoothGroup mesh num smoothNum
						setFaceSmoothGroup mesh (num+1) smoothNum
						num+=2
					)
					smoothNum+=1
					setFaceSmoothGroup mesh num smoothNum
					setFaceSmoothGroup mesh (num+1) smoothNum
					num+=2
					smoothNum=0
				)
				if capEnd==true do
				(
					for i=1 to sides-1 do
					(
						smoothNum+=1
						if smoothNum==3 do
						(
							smoothNum=1
						)
						setFaceSmoothGroup mesh num smoothNum
						setFaceSmoothGroup mesh (num+1) smoothNum
						num+=2
					)
					smoothNum+=1
					setFaceSmoothGroup mesh num smoothNum
					setFaceSmoothGroup mesh (num+1) smoothNum
				)
			)
			4:(
				num=meshop.getNumFaces mesh
				for i=1 to num do
				(
					setFaceSmoothGroup mesh i 0
				)
			)
		)
	)
	tool create
	(
	on mousePoint click do
		case click of
		(
			1: nodeTM.translation = gridPoint
			3: #stop
		)
		on mouseMove click do
		case click of
		(
			2: m = sqrt (gridDist.x^2 + gridDist.y^2)
			3: radius = gridDist.z
		)
	)
)