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
	)
	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" pos:[46,7] width:103 height:16 range:[0,999,5] type:#worldunits
		spinner spn2 "Segments" pos:[32,32] width:117 height:16 range:[3,9999,10] type:#integer
		spinner spn3 "Sides" pos:[53,56] width:96 height:16 range:[3,9999,10] type:#integer
		edittext x_pos "X" pos:[8,80] width:136 height:17
		edittext y_pos "Y" pos:[8,104] width:136 height:17
		edittext z_pos "Z" pos:[8,128] width:136 height:17
		
		colorPicker xT "" pos:[144,80] width:16 height:16 color:(color 0 155 0) enabled:false
		colorPicker yT "" pos:[144,104] width:16 height:16 color:(color 0 155 0) enabled:false
		colorPicker zT "" pos:[144,128] width:16 height:16 color:(color 0 155 0) enabled:false
		
		pickButton obj "Spline" pos:[8,152] width:88 height:26 autodisplay:true filter:pickfilter enabled:false
		checkbox use "Use" pos:[104,160] width:43 height:16
		button genSpline "Generate Spline" pos:[35,182] height:26
		
		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"
		button editPreset "Edit Presets"
		on customValues open do
		(
			fileName=getMAXIniFile()
			presetList=#( )
			if fileName !=undefined then
			(
				if (hasINISetting fileName "SpiroTube" "Presets") then
				(
					Items=#( )
					presetList=execute (getINISetting fileName "SpiroTube" "Presets")
					numPresets=presetList.count
					for i=1 to numPresets do
					(
						Items[i]=presetList[i][1]
					)
					preset.items=items
				)
				else
				(
					--#(Name,#(xPos,yPos,zPos),#([useM,M],[useN,N],[useP,P],[useQ,Q],[useH,H],[useT,T]),#(ScaleType,[Amount,Frequency,Offset],#(UseA,UseF,UseO,UseC),Custom)
					--	#("Torus","Torus_1","Figure Eight Knot","Figure Eight Knot_1","Inner circulating","Granny/square knot","Triple knot")
					preset1=#( )
					preset1[1]="Torus"
					preset1[2]=#("m*cos(360.0*i/segments)","m*sin(360.0*i/segments)","h")
					preset1[3]=#([1,30],[0,0],[0,0],[0,0],[1,0],[0,0])
					preset1[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					preset2=#( )
					preset2[1]="Torus_1"
					preset2[2]=#("m*cos(p*i)+n*cos(q*i)","m*sin(p*i)+n*sin(q*i)","h*sin(t*i)")
					preset2[3]=#([1,30],[1,6],[1,2],[1,-5],[1,2.5],[1,7])
					preset2[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					preset3=#( )
					preset3[1]="Figure Eight Knot"
					preset3[2]=#("m*cos(p*i)+n*cos(q*i)","m*sin(p*i)+n*sin(q*i)","h*sin(t*i)")
					preset3[3]=#([1,15],[1,20],[1,1],[1,3],[1,4.5],[1,4])
					preset3[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					preset4=#( )
					preset4[1]="Figure Eight Knot_1"
					preset4[2]=#("m*cos(p*i)+n*cos(q*i)","m*sin(p*i)+n*sin(q*i)","h*sin(t*i)")
					preset4[3]=#([1,15],[1,18.75],[1,3],[1,5],[1,2.5],[1,8])
					preset4[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					preset5=#( )
					preset5[1]="Inner circulating"
					preset5[2]=#("m*cos(p*i)+n*cos(q*i)","m*sin(p*i)+n*sin(q*i)","h*sin(t*i)")
					preset5[3]=#([1,20],[1,15],[1,2],[1,5],[1,2],[1,6])
					preset5[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					preset6=#( )
					preset6[1]="Granny/square knot"
					preset6[2]=#("m*cos(p*i)-n*cos(q*i)","m*sin(p*i)-n*sin(q*i)","h*sin(t*i)-0.3*sin(6.0*i)")
					preset6[3]=#([1,15],[1,30],[1,1],[1,-3],[1,3],[1,4])
					preset6[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					preset7=#( )
					preset7[1]="Triple knot"
					preset7[2]=#("17.7*cos(1.0*i)+m*cos(p*i)-n*cos(q*i)","17.7*sin(1.0*i)+m*sin(p*i)-n*sin(q*i)","h*sin(t*i)+2.0*sin(6.0*i)")
					preset7[3]=#([1,9],[1,13.5],[1,-2],[1,-5],[1,2],[1,9])
					preset7[4]=#(1,[0,0,0],#(false,false,false,false)," ")
					
					newPresets=#( )
					newPresets[1]=preset1
					newPresets[2]=preset2
					newPresets[3]=preset3
					newPresets[4]=preset4
					newPresets[5]=preset5
					newPresets[6]=preset6
					newPresets[7]=preset7
					
					fileName=getMAXIniFile()
					if fileName !=undefined then
					(
						setINISetting fileName "SpiroTube" "Presets" (newPresets as string)
					)
					else
					(
						messagebox "Presets werent loaded. Defaults werent able to be saved." title:"SpiroTube"
					)
				)
			)
			else
			(
				messagebox "Unable to load presets." title:"SpiroTube"
			)
		)
		on editPreset pressed do
		(
			local Editpreset=undefined
			global xPos1=xPos
			global yPos1=yPos
			global zPos1=zPos
			global m1=m
			global n1=n
			global p1=p
			global q1=q
			global h1=h
			global t1=t
			global type1=type
			global amount1=amount
			global frequency1=frequency
			global offset1=offset
			global custom1=custom
			rollout Editpreset "Preset" width:160 height:104
			(
				local curPreset=1
				local presetList=undefined
				--First part, saving current settings as preset
				edittext name1 "Name " pos:[8,8] width:144 height:17
				checkbox variable1 "Variables" pos:[8,24] width:144 height:16
				checkbox mesh1 "Mesh parameters" pos:[8,48] width:144 height:16
				button save1 "Save" pos:[8,72] width:64 height:24
				button edit1 "Edit" pos:[88,72] width:64 height:24
				
				--Editing presets :\
				dropdownList choose1 "" pos:[8,104] width:144 height:21
				edittext name2 "Name " pos:[8,136] width:144 height:16
				edittext xPos2 "X:" pos:[8,160] width:144 height:16
				edittext yPos2 "Y:" pos:[8,184] width:144 height:16
				edittext zPos2 "Z:" pos:[8,208] width:144 height:16
				
				GroupBox variablesBox "Variables" pos:[8,224] width:144 height:160
				spinner M2 "M:" pos:[48,240] width:96 height:16
				spinner N2 "N:" pos:[48,264] width:96 height:16
				spinner P2 "P:" pos:[48,288] width:96 height:16
				spinner Q2 "Q:" pos:[48,312] width:96 height:16
				spinner H2 "H:" pos:[48,336] width:96 height:16
				spinner T2 "T:" pos:[48,360] width:96 height:16
				checkbox uM2 "" pos:[16,240] width:16 height:16
				checkbox uN2 "" pos:[16,264] width:16 height:16
				checkbox uP2 "" pos:[16,288] width:16 height:17
				checkbox uQ2 "" pos:[16,312] width:16 height:16
				checkbox uH2 "" pos:[16,336] width:16 height:16
				checkbox uT2 "Checkbox" pos:[16,360] width:16 height:16
				
				GroupBox meshBox "Mesh parameters" pos:[8,384] width:144 height:128
				radiobuttons scaleType2 "" pos:[16,400] width:62 height:16 labels:#("1", "2") default:1 columns:2
				spinner sAmount2 "Amount: " pos:[56,416] width:88 height:16
				spinner sFrequency2 "Frequency: " pos:[40,440] width:104 height:16
				spinner sOffset2 "Offset: " pos:[56,464] width:88 height:16
				edittext sCustom2 "" pos:[32,488] width:112 height:16
				checkbox uA2 "" pos:[16,416] width:16 height:16
				checkbox uF2 "Checkbox" pos:[16,440] width:16 height:16
				checkbox uO2 "Checkbox" pos:[16,464] width:16 height:16
				checkbox uC2 "" pos:[16,488] width:16 height:16
				
				button Save2 "Save" pos:[8,520] width:64 height:24
				button Delete2 "Delete" pos:[88,520] width:64 height:24
				on choose1 selected newSel do
				(
					fileName=getMAXIniFile()
					presetList=#( )
					if fileName !=undefined then
					(
						if (hasINISetting fileName "SpiroTube" "Presets") then
						(
							presetList=execute (getINISetting fileName "SpiroTube" "Presets")
							curPreset=presetList[newSel]
							
							name2.text=curPreset[1]
							xPos2.text=curPreset[2][1]
							yPos2.text=curPreset[2][2]
							zPos2.text=curPreset[2][3]
							
							--Variables
							M2.value=curPreset[3][1][2]
							N2.value=curPreset[3][2][2]
							P2.value=curPreset[3][3][2]
							Q2.value=curPreset[3][4][2]
							H2.value=curPreset[3][5][2]
							T2.value=curPreset[3][6][2]
							if curPreset[3][1][1]==0 then
							(
								uM2.state=false
							)
							else
							(
								uM2.state=true
							)
							if curPreset[3][2][1]==0 then
							(
								uN2.state=false
							)
							else
							(
								uN2.state=true
							)
							if curPreset[3][3][1]==0 then
							(
								uP2.state=false
							)
							else
							(
								uP2.state=true
							)
							if curPreset[3][4][1]==0 then
							(
								uQ2.state=false
							)
							else
							(
								uQ2.state=true
							)
							if curPreset[3][5][1]==0 then
							(
								uH2.state=false
							)
							else
							(
								uH2.state=true
							)
							if curPreset[3][6][1]==0 then
							(
								uT2.state=false
							)
							else
							(
								uT2.state=true
							)
							
							--Mesh parameters
							scaleType2.state=curPreset[4][1]
							sAmount2.value=curPreset[4][2][1]
							sFrequency2.value=curPreset[4][2][2]
							sOffset2.value=curPreset[4][2][3]
							uA2.state=curPreset[4][3][1]
							uF2.state=curPreset[4][3][2]
							uO2.state=curPreset[4][3][3]
							uC2.state=curPreset[4][3][4]
							sCustom2.text=curPreset[4][4]
							curPreset=newSel
						)
						else
						(
							messagebox "Unable to load presets." title:"SpiroTube"
						)
					)
					else
					(
						messagebox "Unable to load presets." title:"SpiroTube"
					)
				)
				on save1 pressed do
				(
					curPreset=presetList.count+1
					newPreset=#( )
					newPreset[1]=name1.text
					newPreset[2]=#( )
					newPreset[2][1]=xPos1
					newPreset[2][2]=yPos1
					newPreset[2][3]=zPos1
					
					--Variables...
					newPreset[3]=#( )
					newPreset[3][1]=[0,0]
					newPreset[3][2]=[0,0]
					newPreset[3][3]=[0,0]
					newPreset[3][4]=[0,0]
					newPreset[3][5]=[0,0]
					newPreset[3][6]=[0,0]
					newPreset[3][1][2]=m1
					newPreset[3][2][2]=n1
					newPreset[3][3][2]=p1
					newPreset[3][4][2]=q1
					newPreset[3][5][2]=h1
					newPreset[3][6][2]=t1
					for i=1 to 6 do
					(
						if variable1.state==true then
						(
							newPreset[3][i][1]=1
						)
						else
						(
							newPreset[3][i][1]=0
						)
					)
					
					--Mesh parameters...
					newPreset[4]=#( )
					newPreset[4][1]=type1
					newPreset[4][2]=[0,0,0]
					newPreset[4][2][1]=Amount1
					newPreset[4][2][2]=Frequency1
					newPreset[4][2][3]=Offset1
					newPreset[4][3]=#( )
					newPreset[4][3][1]=mesh1.state
					newPreset[4][3][2]=mesh1.state
					newPreset[4][3][3]=mesh1.state
					newPreset[4][3][4]=mesh1.state
					if custom1==undefined then
					(
						newPreset[4][4]=" "
					)
					else
					(
						newPreset[4][4]=custom1
					)
					
					presetList[curPreset]=newPreset
					fileName=getMAXIniFile()
					if fileName !=undefined then
					(
						setINISetting fileName "SpiroTube" "Presets" (presetList as string)
					)
					else
					(
						messagebox "Unable to save presets." title:"SpiroTube"
					)
					items=choose1.items
					items[items.count+1]=name1.text
					choose1.items=items
				)
				on Delete2 pressed do
				(
					fileName=getMAXIniFile()
					presetList=#( )
					if fileName !=undefined then
					(
						if (hasINISetting fileName "SpiroTube" "Presets") then
						(
							items=choose1.items
							curSel=choose1.selection
							newSel=undefined
							if curSel==1 then
							(
								if items.count==1 then
								(
									newSel=undefined
								)
								else
								(
									newSel=2
								)
							)
							else
							(
								newSel=1
							)
							if newSel !=undefined do
							(
								presetList=execute (getINISetting fileName "SpiroTube" "Presets")
								curPreset=presetList[newSel]
								name2.text=curPreset[1]
								xPos2.text=curPreset[2][1]
								yPos2.text=curPreset[2][2]
								zPos2.text=curPreset[2][3]
								
								--Variables
								M2.value=curPreset[3][1][2]
								N2.value=curPreset[3][2][2]
								P2.value=curPreset[3][3][2]
								Q2.value=curPreset[3][4][2]
								H2.value=curPreset[3][5][2]
								T2.value=curPreset[3][6][2]
								if curPreset[3][1][1]==0 then
								(
									uM2.state=false
								)
								else
								(
									uM2.state=true
								)
								if curPreset[3][2][1]==0 then
								(
									uN2.state=false
								)
								else
								(
									uN2.state=true
								)
								if curPreset[3][3][1]==0 then
								(
									uP2.state=false
								)
								else
								(
									uP2.state=true
								)
								if curPreset[3][4][1]==0 then
								(
									uQ2.state=false
								)
								else
								(
									uQ2.state=true
								)
								if curPreset[3][5][1]==0 then
								(
									uH2.state=false
								)
								else
								(
									uH2.state=true
								)
								if curPreset[3][6][1]==0 then
								(
									uT2.state=false
								)
								else
								(
									uT2.state=true
								)
								
								--Mesh parameters
								scaleType2.state=curPreset[4][1]
								sAmount2.value=curPreset[4][2][1]
								sFrequency2.value=curPreset[4][2][2]
								sOffset2.value=curPreset[4][2][3]
								uA2.state=curPreset[4][3][1]
								uF2.state=curPreset[4][3][2]
								uO2.state=curPreset[4][3][3]
								uC2.state=curPreset[4][3][4]
								sCustom2.text=curPreset[4][4]
								curPreset=newSel
								newItems=deleteItem items curSel
								choose1.items=newItems
								if newSel !=undefined do
								(
									choose1.selection=newSel
								)
								newList=deleteItem presetList curPreset
								setINISetting fileName "SpiroTube" "Presets" (newList as string)
							)
						)
						else
						(
							messagebox "Unable to load presets." title:"SpiroTube"
						)
					)
					else
					(
						messagebox "Unable to load presets." title:"SpiroTube"
					)
				)
				on save2 pressed do
				(
					newPreset=#( )
					newPreset[1]=name2.text
					newPreset[2]=#( )
					newPreset[2][1]=xPos2.text
					newPreset[2][2]=yPos2.text
					newPreset[2][3]=zPos2.text
					
					--Variables...
					newPreset[3]=#( )
					newPreset[3][1]=[0,0]
					newPreset[3][2]=[0,0]
					newPreset[3][3]=[0,0]
					newPreset[3][4]=[0,0]
					newPreset[3][5]=[0,0]
					newPreset[3][6]=[0,0]
					if uM2.state==true then
					(
						newPreset[3][1][1]=1
					)
					else
					(
						newPreset[3][1][1]=0
					)
					newPreset[3][1][2]=m2.value
					
					if uN2.state==true then
					(
						newPreset[3][2][1]=1
					)
					else
					(
						newPreset[3][2][1]=0
					)
					newPreset[3][2][2]=n2.value
					
					if uP2.state==true then
					(
						newPreset[3][3][1]=1
					)
					else
					(
						newPreset[3][3][1]=0
					)
					newPreset[3][3][2]=p2.value
					
					if uQ2.state==true then
					(
						newPreset[3][4][1]=1
					)
					else
					(
						newPreset[3][4][1]=0
					)
					newPreset[3][4][2]=q2.value
					
					if uH2.state==true then
					(
						newPreset[3][5][1]=1
					)
					else
					(
						newPreset[3][5][1]=0
					)
					newPreset[3][5][2]=h2.value
					
					if uT2.state==true then
					(
						newPreset[3][6][1]=1
					)
					else
					(
						newPreset[3][6][1]=0
					)
					newPreset[3][6][2]=t2.value
					
					--Mesh parameters...
					newPreset[4]=#( )
					newPreset[4][1]=scaleType2.state
					newPreset[4][2]=[0,0,0]
					newPreset[4][2][1]=sAmount2.value
					newPreset[4][2][2]=sFrequency2.value
					newPreset[4][2][3]=sOffset2.value
					newPreset[4][3]=#( )
					newPreset[4][3][1]=uA2.state
					newPreset[4][3][2]=uF2.state
					newPreset[4][3][3]=uO2.state
					newPreset[4][3][4]=uC2.state
					newPreset[4][4]=sCustom2.text
					
					presetList[curPreset]=newPreset
					fileName=getMAXIniFile()
					if fileName !=undefined then
					(
						setINISetting fileName "SpiroTube" "Presets" (presetList as string)
					)
					else
					(
						messagebox "Unable to save presets." title:"SpiroTube"
					)
					items=choose1.items
					items[curPreset]=newPreset[1]
					choose1.items=items
				)
				on Editpreset open do
				(
					fileName=getMAXIniFile()
					presetList=#( )
					if fileName !=undefined then
					(
						if (hasINISetting fileName "SpiroTube" "Presets") then
						(
							Items=#( )
							presetList=execute (getINISetting fileName "SpiroTube" "Presets")
							numPresets=presetList.count
							for i=1 to numPresets do
							(
								curPreset=presetList[i]
								
								Items[i]=curPreset[1]
								
								name2.text=curPreset[1]
								xPos2.text=curPreset[2][1]
								yPos2.text=curPreset[2][2]
								zPos2.text=curPreset[2][3]
								
								--Variables
								M2.value=curPreset[3][1][2]
								N2.value=curPreset[3][2][2]
								P2.value=curPreset[3][3][2]
								Q2.value=curPreset[3][4][2]
								H2.value=curPreset[3][5][2]
								T2.value=curPreset[3][6][2]
								if curPreset[3][1][1]==0 then
								(
									uM2.state=false
								)
								else
								(
									uM2.state=true
								)
								if curPreset[3][2][1]==0 then
								(
									uN2.state=false
								)
								else
								(
									uN2.state=true
								)
								if curPreset[3][3][1]==0 then
								(
									uP2.state=false
								)
								else
								(
									uP2.state=true
								)
								if curPreset[3][4][1]==0 then
								(
									uQ2.state=false
								)
								else
								(
									uQ2.state=true
								)
								if curPreset[3][5][1]==0 then
								(
									uH2.state=false
								)
								else
								(
									uH2.state=true
								)
								if curPreset[3][6][1]==0 then
								(
									uT2.state=false
								)
								else
								(
									uT2.state=true
								)
								
								--Mesh parameters
								scaleType2.state=curPreset[4][1]
								sAmount2.value=curPreset[4][2][1]
								sFrequency2.value=curPreset[4][2][2]
								sOffset2.value=curPreset[4][2][3]
								uA2.state=curPreset[4][3][1]
								uF2.state=curPreset[4][3][2]
								uO2.state=curPreset[4][3][3]
								uC2.state=curPreset[4][3][4]
								sCustom2.text=curPreset[4][4]
							)
							choose1.items=items
							curPreset=1
						)
						else
						(
							messagebox "Unable to load presets." title:"SpiroTube"
						)
					)
					else
					(
						messagebox "Unable to load presets." title:"SpiroTube"
					)
				)
				on edit1 pressed do
				(
					if Editpreset.height==548 then
					(
						Editpreset.height=104
					)
					else
					(
						Editpreset.height=548
					)
				)
			)
			createdialog Editpreset
		)
		on preset selected newSel do 
		(
			fileName=getMAXIniFile()
			if fileName !=undefined then
			(
				if (hasINISetting fileName "SpiroTube" "Presets") then
				(
					presetList=#( )
					presetList=execute (getINISetting fileName "SpiroTube" "Presets")
					newPreset=presetList[newSel]
					xPos=newPreset[2][1]
					yPos=newPreset[2][2]
					zPos=newPreset[2][3]
					
					--Variables
					M=newPreset[3][1][2]
					N=newPreset[3][2][2]
					P=newPreset[3][3][2]
					Q=newPreset[3][4][2]
					H=newPreset[3][5][2]
					T=newPreset[3][6][2]
					if newPreset[3][1][1]==1 do
					(
						M=newPreset[3][1][2]
					)
					if newPreset[3][2][1]==1 do
					(
						N=newPreset[3][2][2]
					)
					if newPreset[3][3][1]==1 do
					(
						P=newPreset[3][3][2]
					)
					if newPreset[3][4][1]==1 do
					(
						Q=newPreset[3][4][2]
					)
					if newPreset[3][5][1]==1 do
					(
						H=newPreset[3][5][2]
					)
					if newPreset[3][6][1]==1 do
					(
						T=newPreset[3][6][2]
					)
					
					--Mesh parameters
					num1=0
					if newPreset[4][3][1]==true do
					(
						Amount=newPreset[4][2][1]
						num1+=1
						Type=newPreset[4][1]
					)
					if newPreset[4][3][2]==true do
					(
						Frequency=newPreset[4][2][2]
						num1+=1
						Type=newPreset[4][1]
					)
					if newPreset[4][3][3]==true do
					(
						Offset=newPreset[4][2][3]
						num1+=1
						Type=newPreset[4][1]
					)
					--#(Name,#(xPos,yPos,zPos),#([useM,M],[useN,N],[useP,P],[useQ,Q],[useH,H],[useT,T]),#(ScaleType,[Amount,Frequency,Offset],#(UseA,UseF,UseO,UseC),Custom))
					if newPreset[4][3][4]==true do
					(
						Custom=newPreset[4][4]
						num1+=1
						Type=newPreset[4][1]
					)
				)
				else
				(
					messagebox "Unable to load presets." title:"SpiroTube"
				)
			)
			else
			(
				messagebox "Unable to load presets." title:"SpiroTube"
			)
		)
	)
	rollout tubeParams "Mesh 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 range:[-999,999,2.5] type:#worldunits
		spinner f1 "Frequency: " pos:[32,88] width:120 height:16 range:[0,999,4] type:#float
		spinner o1 "Offset: " pos:[32,111] width:120 height:16 type:#float range:[-9999,9999,0]
		edittext c1 "" pos:[16,152] width:136 height:17
		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:[-9999,9999,0]
		radioButtons smoothType "Smoothing" labels:#("All","Segments","Sides","None") columns:1 align:#left
		on tubeParams open 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
	(
		--Globals for positions...
		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
		
		--Making sure the strings are valid to use
		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--Positions from spline(currently not used)
		(
			seg=numKnots obj 1
			segments=seg
			for i=1 to seg do
			(
				newPos=getKnotPoint obj 1 i
				pos[pos.count+1]=newPos
			)
		)
		else
		(
			Results=#( )
			results[1]=false
			results[2]=false
			results[3]=false
			for j=1 to segments do--Positions from functions
			(
				curPos=[0,0,0]
				pos1=execute string1
				pos2=execute string2
				pos3=execute string3
				--Checking the result is a number...
				if (isKindOf pos1 number)==true do
				(
					curPos[1]=pos1
					Results[1]=true
				)
				if (isKindOf pos2 number)==true do
				(
					curPos[2]=pos2
					Results[2]=true
				)
				if (isKindOf pos3 number)==true do
				(
					curPos[3]=pos3
					Results[3]=true
				)
				pos[pos.count+1]=curPos
				i+=1
			)
			if results[1]==true then
			(
				params.xT.color=(color 0 255 0)
			)
			else
			(
				params.xT.color=(color 255 0 0)
			)
			if results[2]==true then
			(
				params.yT.color=(color 0 255 0)
			)
			else
			(
				params.yT.color=(color 255 0 0)
			)
			if results[3]==true then
			(
				params.zT.color=(color 0 255 0)
			)
			else
			(
				params.zT.color=(color 255 0 0)
			)
		)
		vectors=#( )
		vectors[1]=[0,0,0]
		
		--Getting vectors around circle
		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)
		)
		--Start/end vector
		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
		--Creating actual vertices
		for k=1 to segments do
		(
			newPos=pos[k]--Getting current pos
			sVector=cross [0,0,1] vectors[k]--Getting first vector for positions
			bias=0
			for j=1 to sides do
			(
				--Bias and rotation
				if twistBias >=0 then
				(
					bias1=((k as float)/(segments as float))^twistBias
					rot1=rotationAmount+(((twistamount*360.0)/segments as float)*k)*bias1
				)
				else
				(
					bias2=twistBias*-1
					bias1=(((segments-k+1.0) as float)/(segments as float))^bias2
					rot1=rotationAmount+(((twistamount*360.0)/segments)*(segments-k+1.0))*bias1
				)
				--Rotating the starting vector, so it gives each point in the circle
				newV=rotateVector sVector vectors[k] ((rotateAmount*j)+rot1)
				newRadius=radius
				if type==1 then--using settings for scale...
				(
					newRadius=radius+(amount*cos((((frequency*360.0)/segments)*(k as float))+offset))
				)
				else--Using custom function for scale...
				(
					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)--Adding the position to the vertice list.
			)
			--print bias
			i+=1
		)
		faces=#( )
		curNum=1
		tempNum=0
		for i=1 to segments-1 do--Creating faces
		(
			tempNum=0
			for j=1 to sides-1 do--All but 1 face
			(
				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
			)
			--The last face
			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--Removing edges
		(
			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--Start/end edges
		(
			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--Smoothing faces...
		(
			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
		)
	)
)