--Created by Norman Schaar: norman3d@gmail.com

macroScript FiberMeshToSpline
category:"Norman3D"
buttontext:"FiberMesh to Spline"
tooltip:"Convert Fiber Mesh to Spline"
(

if selection.count != 1 then
(
	messagebox "You have to select a mesh"
)
else
(
	theMesh = selection[1]
	if classof theMesh == Editable_Poly then
	(
		start = timeStamp()
		
		with undo off with redraw off
		(
			progressStart "Creating splines..."
			theMesh = selection[1]
			
			--Get total vert count of mesh
			theVertCount = polyop.getNumVerts theMesh
			--Get total face count of mesh
			theFacesCount = theMesh.getNumFaces()

			--A bitArray to hold all the face IDs that were either checked or were part of elements of other checked faces.
			usedFaces = #{}
			
			--These variables we only need to get once. Each hair strand has the same amount of verts and sides.
			global theSidesCount = undefined
			global theVerts = undefined
			
			--All the faces
			theFaces = #{1..theFacesCount}

			--We Create a splineshape
			sp = splineShape()
			--We will be keeping track of the element number
			local ElementNumber = 0
			
			--This is for the progressbar
			global progressVertCount = 0
			
			--For each face in "all the faces" that has not been checked yet:
			for f in theFaces where not usedFaces[f] do
			(
				ElementNumber = ElementNumber + 1
				local elem = polyop.getElementsUsingFace theMesh #{f}
				usedFaces += elem --We add the faces of the current element to the bitarray so we won't check the remaining faces.
				
				--We get the edges using the faces. We will need the first edge of the elemet.
				local TheEdges = polyop.getEdgesUsingFace theMesh elem
				--We get the Vertcount of an element.
				if theVerts == undefined do
				(
					local theVerts = polyop.getVertsUsingEdge theMesh theEdges
					global VertCount = theVerts.numberSet
				)
				--We update the progressbar
				global progressVertCount = progressVertCount + VertCount
				progressUpdate (progressVertCount / (theVertCount as float)  * 100.0)

				--We get the first edge in "theEdges" bitarray. This seems to be the fastest way.
				local theEdge = (theEdges as array)[1]
				
				--Here are some other methods I tried:
				/*
					--Method 1: (Causes garbage collection error)
					local theEdge = for n in TheEdges do exit with n
				
					--Method 2: (It's fast but ugly)
					local theString = theEdges as string
					local TheEdge = (substring theString 3 ((findString theString "..") - 3)) as integer
					local TheEdge = (filterString (TheEdges as string) ".")[1] as integer
				
					--Method 3: (Proposed by DenisT, slower than "(theEdges as array)[1]"
					fn firstBit arr = 
					(
						local b
						for n in arr while (b = n; off) do ()
						b
					)
					local TheEdge = firstBit theEdges
				*/
				
				
				--Get hair sides count
				if theSidesCount == undefined do
				(
					--We select the first edge
					theMesh.EditablePoly.SetSelection #Edge #{TheEdge}
					--We click loop (Is there a better way of doing this?)
					theMesh.EditablePoly.buttonOp #SelectEdgeLoop
					global theSidesCount = theMesh.EditablePoly.ConvertSelection #Edge #Vertex
				)
				
				
				--The Hair segments: Since we have the total vertcount and the amount of vertices per segment, we can figure out how many segments there are.
				local theSegments = VertCount / theSidesCount
				
				--We add a spline for the element.
				local theSplineIndex = addnewspline sp

				--For each segment in the hair element, create a knot
				for i=1 to (theSegments) do
				(
					--The vert number of every vert of a segment
					local FirstVert = (((ElementNumber - 1) * VertCount) + 1) + ((i - 1) * theSidesCount)
					local LastVert = ((ElementNumber - 1) * VertCount) + ((i - 1) * theSidesCount) + theSidesCount
					
					--Get an array of the vert numbers of the particular segment
					local theVertexIdArray = for j=FirstVert to LastVert collect j
					
					--Now we need to calculate the knot's position in space. We sum the segment's vertices position and calculate the average. This way the knot is in the center of the vertices.
					local sumVal = [0,0,0]
					for val in theVertexIdArray do (sumVal += (polyop.GetVert theMesh val))
					local sumVal = sumVal/theSidesCount
					
					--Adding the knot
					addknot sp theSplineIndex #corner #line  sumVal
				)
			)
			--After all the splines have been aded we update the shape, change the pivot point, and that's it!
			updateshape sp
			sp.pivot = theMesh.pivot
			progressEnd()
			
			end = timeStamp()

			format "Processing took % seconds\n" ((end - start) / 1000.0)
		)
	)
	else
	(
		messagebox "The selected mesh must be an Editable Poly"
	)
)
)