# Name :          Lathe 1.0
# Description :   lathe selected edges/components around the z-axis (if "turns" is a negative number, rotation is clockwise)
# Author :        Frank Wiesner
# Usage :         1. select edges (and Components)
#                 2. run script
# Date :          31.Aug.2oo4
# Type :          script
# History:        
#                 1.o (31.Aug.2oo4) - based on screw 2.1; first version

require 'sketchup.rb'

class Lathe

def initialize
    @turns = "360.0"
    @steps = "10.0"
end

def spin_selection
	mo=Sketchup.active_model()
	ss = mo.selection()

	if ss.empty? 
		UI.messagebox("Nothing selected!")
	else
		prompts = ["Steps", "Degrees"]
		values = [@steps, @turns]
		results = inputbox prompts, values, "Lathe"
		temp1, temp2 = results
		if (temp1 != false)
			@steps = temp1
			@turns = temp2

			steps = eval(@steps)
			deg = eval(@turns)
			
			deg = 360.0 if (deg>360.0)
			deg = -360.0 if (deg<-360.0)

			turns = deg/360.0
			
			# neg. steps not allowed!
			if (steps < 0) 
				steps = -1*steps
			end
			
			rotDir = 1
			if (turns < 0) 
				turns = -1*turns
				rotDir = -1
			end
			
			angularOffset = 360.degrees/(steps.floor)
			angularOffsetComponent = 360.degrees/steps
			targetAngle = 360.degrees*turns

			# copy all relevant points in an array...
			
			verticesArray = Array.new()
			edgeArray = Array.new()
			compArray = Array.new()
			constrPtsArray = Array.new()
			
			ss.each {|e| 
				if (e.kind_of? Sketchup::Edge)
					verticesArray.push(e.vertices) 
					edgeArray.push(e) 
				end
				compArray.push(e) if e.kind_of? Sketchup::ComponentInstance
				constrPtsArray(e.position) if e.kind_of? Sketchup::ConstructionPoint
			}
			if (edgeArray.empty? && compArray.empty?)
				# UI.messagebox("At least one edge or component must be selected!")
			else
				verticesArray.flatten!
				ptArray = Array.new()
				verticesArray.each do |v|
					ptArray.push(Geom::Point3d.new(v.position))
				end
				
				# determin vertical and radial Offset
				
				
				fullOffsetVector = Geom::Vector3d.new(0, 0, 0) 

				faceOrientation = 1
				faceOrientation = -1 if (fullOffsetVector.z<0)
				xOff = fullOffsetVector.x/steps
				yOff = fullOffsetVector.y/steps
				zOff = fullOffsetVector.z/steps

				totalNumberOfOperations = steps*turns*(ptArray.length/2)+steps*turns
				doneNumberOfOperations = 0

				# now we can do the transformations...
				# we build a 2D Array for all points
				
				mo.start_operation "Lathe"
				
				# spinning Edges and Construction Points...

				newPtsArray = Array.new()
				oldPtsArray = ptArray;
				newConPtsArray = Array.new()
				oldConPtsArray = constrPtsArray;

				targetAngle = 360.degrees if !fullOffsetVector.valid? 

				i = 1
				while (i*angularOffset < targetAngle)
					
					# defining transformations

					rot=Geom::Transformation.rotation(ORIGIN, Z_AXIS, i*angularOffset*rotDir)

					# transform Edge Points

					newPtsArray = Array.new()
					for k in 0..(ptArray.length-1) 
						newpt=Geom::Point3d.new([ptArray[k].x ,ptArray[k].y ,ptArray[k].z])
						newpt.transform!(rot) 
						newPtsArray[k] = newpt
						if ((k%2)==1)
							make_edges_and_faces(newPtsArray[k-1], newPtsArray[k], oldPtsArray[k-1], oldPtsArray[k], angularOffset*rotDir, faceOrientation)
						end
					end
					oldPtsArray = newPtsArray

					# now the Construction Points

					newConPtsArray = Array.new()
					for k in 0..(constrPtsArray.length-1) 
						newpt=Geom::Point3d.new([constrPtsArray[k].x ,constrPtsArray[k].y ,constrPtsArray[k].z])
						newpt.transform!(rot) 
						newConPtsArray[k] = newpt
						mo.entities.add_edges(oldConPtsArray[k], newConPtsArray[k])
					end
					oldConPtsArray = newConPtsArray
					
					doneNumberOfOperations = doneNumberOfOperations + (ptArray.length/2)
					Sketchup.set_status_text("lathe: spinning edges ("+String((100*Float(doneNumberOfOperations)/Float(totalNumberOfOperations)).floor)+"%)") 
				
					i = i+1
				end

				# doing ramaining edges (fractional part)
				
				# defining transformations

				rot=Geom::Transformation.rotation(ORIGIN, Z_AXIS, i*angularOffset*rotDir)
				rotAux=Geom::Transformation.rotation(ORIGIN, Z_AXIS, targetAngle)

				# transform Edge Points

				newPtsArray = Array.new()
				for k in 0..(ptArray.length-1) 
					newpt=Geom::Point3d.new([ptArray[k].x ,ptArray[k].y ,ptArray[k].z])
					newpt.transform!(rot) 
					aux=Geom::Point3d.new([ptArray[k].x ,ptArray[k].y ,ptArray[k].z])
					aux.transform!(rotAux) 
					slicePlane = [aux, Z_AXIS.cross(ORIGIN-aux)]
					sliceLine = [newpt, oldPtsArray[k]] 
					newPtsArray[k] = Geom.intersect_line_plane(sliceLine,slicePlane)
					if ((k%2)==1)
						make_edges_and_faces(newPtsArray[k-1], newPtsArray[k], oldPtsArray[k-1], oldPtsArray[k], angularOffset*rotDir, faceOrientation)
					end
				end
				oldPtsArray = newPtsArray

				
				# transform Construction Points

				newConPtsArray = Array.new()
				for k in 0..(constrPtsArray.length-1) 
					newpt=Geom::Point3d.new([constrPtsArray[k].x ,constrPtsArray[k].y ,constrPtsArray[k].z])
					newpt.transform!(rot) 
					aux=Geom::Point3d.new([ptArray[k].x ,ptArray[k].y ,ptArray[k].z])
					aux.transform!(rotAux) 
					slicePlane = [aux, Z_AXIS.cross(ORIGIN-aux)]
					sliceLine = [newpt, oldPtsArray[k]] 
					newConPtsArray[k] = Geom.intersect_line_plane(sliceLine,slicePlane)
					mo.entities.add_edges(oldConPtsArray[k], newConPtsArray[k])
				end
				oldConPtsArray = newConPtsArray

				# spinning Components...

				targetAngle = 360.degrees-0.001 if !fullOffsetVector.valid? 

				i = 1
				while (i*angularOffsetComponent < targetAngle)
					
					# defining transformations

					rot=Geom::Transformation.rotation(ORIGIN, Z_AXIS, i*angularOffsetComponent*rotDir)

					# insert Components and transform...

					compArray.each do |c|
						nc = mo.entities.add_instance(c.definition, c.transformation) 
						nc.transform!(rot)
					end

					doneNumberOfOperations = doneNumberOfOperations + 1
					Sketchup.set_status_text("lathe: spinning components ("+String((100*Float(doneNumberOfOperations)/Float(totalNumberOfOperations)).floor)+"%)") 

					i = i+1
				end

				mo.commit_operation
				Sketchup.set_status_text("lathe: ready.") 
			end 
		end 
	end
end

def get_next_edge(e,v)
	conEdges = v.edges
	return nil if (conEdges.length < 2) 
	return conEdges[1] if (e == conEdges[0]) 
	return conEdges[0]
end

def make_edges_and_faces(p1, p2, p3, p4, ao, fo)
	mo=Sketchup.active_model()
	lines = Array.new()
	lines.push(mo.entities.add_edges(p1, p2).first)
	lines.push(mo.entities.add_edges(p4, p1).first)
	lines.push(mo.entities.add_edges(p3, p1).first)
	lines.push(mo.entities.add_edges(p4, p2).first)
	# test for colinear to avoid "add_face': Points are not planar" errors
	notColinear = true;
	vec1 = p3 - p4
	vec2 = p3 - p1
	notColinear = vec1.cross(vec2).valid?
	if (notColinear)
		if (ao*fo < 0) 
			mo.entities.add_face(p3, p4, p1)
			mo.entities.add_face(p4, p1, p2)
		else
			mo.entities.add_face(p3, p1, p4)
			mo.entities.add_face(p4, p2, p1)
		end
	end
	#smoothing edges...
	lines.each do |e|
		e.smooth=true
		e.soft=true
	end
end

end #class

latheTool = Lathe.new

if( not file_loaded?("lathe.rb") )
	UI.menu("Plugins").add_item("Lathe 1.0") { latheTool.spin_selection }
end
file_loaded("lathe.rb")

