# copyright= 'Huynh Duong Phuong Vi @ 2013-2014'
# suforyou.vn@gmail.com
module S4U
module S4u_AlignTool 
class AlignTool
def initialize(alignfix=0)  
	@alignfix=alignfix    
end
def activate  
	@e1=[]
	@e2=[]
	@strings=S4U::S4u_AlignTool::Strings   
	self.reset(nil)
	model=Sketchup.active_model
	sels=model.selection
	@selected_objects=nil
	@selected_edges=[];@selected_faces=[]
	model.start_operation "Align",true,true,false
	if !sels.empty?  
		objects=[]
		sels.each{|s|
				if s.is_a?Sketchup::Edge
						@selected_edges<< s
				elsif s.is_a?Sketchup::Face
						@selected_faces<< s
				else
						objects << s
				end
		}
		@selected_objects =model.active_entities.add_group objects if not objects.empty? 		
	end
	model.commit_operation
	self.setstatus(nil)       
end
def deactivate(view)
	model = Sketchup.active_model
	model.start_operation "Align",true,false,true
	@selected_objects.explode if @selected_objects
	model.commit_operation
end
def setstatus(view)
	if @align_mode== 1
	  if  not @selectedge1
		if @e1.empty?
			if @alignfix==0
				Sketchup.status_text = @strings.GetString("Select target edge -> align || press TAB -> align+move")
			else
				Sketchup.status_text = @strings.GetString("Select target edge -> align+move || press TAB -> align")
			end
		else
			if @alignfix==0              
				  Sketchup.status_text=@strings.GetString("Select aligned edges || press TAB -> align+move || press UP,DOWN or Right Mouse -> opposite Align")
			else   
				Sketchup.status_text=@strings.GetString("Select align+moved edges || press TAB -> align || press UP,DOWN or Right Mouse -> opposite Align")
			end
		end
	else
		if @selectedge2.empty? 
			if @alignfix==0
				Sketchup.status_text = @strings.GetString("Select aligned edges || press hold Ctrl(Alt on Mac) -> multiselect")
			else
				Sketchup.status_text = @strings.GetString("Select align+moved edges || press hold Ctrl(Alt on Mac) -> multiselect")
			end  
	   elsif @mulsel==1          
			if @alignfix==0
				Sketchup.status_text = @strings.GetString("Select aligned edges || Double-Click -> finish") 
			else   
				Sketchup.status_text = @strings.GetString("Select align+moved edges || Double-Click -> finish")
			end          
		end
	end
	elsif @align_mode== 2
		if not @selectface1
			if @e2.empty?
				if @alignfix==0
					Sketchup.status_text = @strings.GetString("Select target face -> align || press TAB -> align+move")
				else
					Sketchup.status_text = @strings.GetString("Select target face -> align+move || press TAB -> align")
				end
			else 
				if @alignfix==0              
					Sketchup.status_text=@strings.GetString("Select aligned faces || press TAB -> align+move || press UP,DOWN or Right Mouse -> opposite Align")
				else   
					Sketchup.status_text=@strings.GetString("Select align+moved faces || press TAB -> align || press UP,DOWN or Right Mouse -> opposite Align")
				end           
			end       
		else    
			if @selectface2.empty?
				if @alignfix==0
					Sketchup.status_text = @strings.GetString("Select aligned faces || press hold Ctrl(Alt on Mac) for multiselect")
				else
					Sketchup.status_text = @strings.GetString("Select align+moved faces || press hold Ctrl(Alt on Mac) for multiselect")
				end         
			else
			   if @mulsel==1
				  if @alignfix==0
					 Sketchup.status_text = @strings.GetString("Select aligned faces || Double-Click for finish") 
				  else   
					 Sketchup.status_text = @strings.GetString("Select align+moved faces || Double-Click for finish")
				  end
			   end                               
			end
		end
	elsif (not @selectface1) || (not @selectedge1)
		if @alignfix==0              
			Sketchup.status_text=@strings.GetString("Select aligned Edges(or Faces) || press TAB -> align+move || press UP,DOWN or Right Mouse -> opposite Align")
		else   
			Sketchup.status_text=@strings.GetString("Select align+moved Edges(or Faces) || press TAB -> align || press UP,DOWN or Right Mouse -> opposite Align")
		end
	end
	view.invalidate if view
end
def draw(view) 	
	 self.draw_over(view)     
	 if @align_mode==1
		self.draw_edge_select(view)  
	 elsif @align_mode==2
		self.draw_face_select(view)
	 end 
end
def draw_edge_select(view)    
	view.line_width = 4
	if @selectedge1      
		view.drawing_color = "blue"         
		view.draw_line(@selectedge1[0],@selectedge1[1]) 
	end    
	if not @pedge2.empty?
		view.drawing_color = "blueviolet"
		@pedge2.each{ |p| view.draw_line(p[0],p[1]) }
	end
end
def draw_face_select(view)
	view.line_width = 4
	view.drawing_color = "blue"
	view.draw_polyline(@ps1) if @selectface1 
	if not @selectface2.empty?
		view.drawing_color = "blueviolet"
		(0..@selectface2.length-1).each{ |i|	view.draw_polyline @ps2[i] }
	end
end
def draw_over(view)
	view.drawing_color = "red"
	view.line_width = 4
	if @align_mode!=2 && @selectedge
		view.set_color_from_line(@selectedge[0],@selectedge[1]) if @select_axis
		view.draw_line(@selectedge[0],@selectedge[1])
	elsif @align_mode!=1 && @selectface
		view.draw_polyline @ps
	end
end
def onMouseMove(flags, x, y, view)     
	ip = view.inputpoint x, y
	selectedge=ip.edge 
	selectface=ip.face 
	trans=ip.transformation 
	ph = view.pick_helper
	ph.do_pick x,y
	element = ph.best_picked
	tedit=view.model.edit_transform
	ip1 = view.inputpoint(x, y,Sketchup::InputPoint.new(tedit.origin))
	p=ip1.position.transform tedit.inverse
	@ps=[];@selectface=nil;@selectedge=nil;@select_axis=nil
	if selectedge#.is_a?Sketchup::Edge
		pstart=selectedge.start.position.clone.transform! trans 
		pend=selectedge.end.position.clone.transform! trans
		@selectedge=[pstart,pend]
		@element=element
	elsif element.is_a?Sketchup::ConstructionLine
		vector = element.direction
		length= view.pixels_to_model 100, ORIGIN
		if vector.length>0
			vector.length=length
			pstart=ip.position.clone + vector
			pend=ip.position.clone + vector.reverse
			@selectedge=[pstart,pend]
			@element=element
		end
	elsif (p.x==0 && p.y==0) || (p.x==0 && p.z==0) || (p.y==0 && p.z==0)
		@select_axis=true
		vector =tedit.origin.vector_to ip1.position
		length= view.pixels_to_model 100, ORIGIN
		if vector.length>0
			vector.length=length
			pstart=ip1.position.clone + vector
			pend=ip1.position.clone + vector.reverse
			@selectedge=[pstart,pend]
			@element=element
		end
	elsif selectface
		ps=selectface.outer_loop.vertices.collect{|p| p.position.transform trans }
		ps<< ps[0]
		if Geom.point_in_polygon_2D(ip.position, ps, true )
			@ps=ps
			@selectface=selectface
			@trans=trans
			@element=element
		end
	end
	self.setstatus(view)
end
def onLButtonDoubleClick(flags, x, y, view)
	self.align_edgeonedge if @selectedge1 && (not @selectedge2.empty?) && @align_mode==1 && @mulsel==1
	self.align_faceonface if @selectface1 && (not @selectface2.empty?) && @align_mode==2 && @mulsel==1
	self.setstatus(view)
end
def onLButtonDown(flags, x, y, view)  
		@mulsel= 1  if @ctrl_down_time && (not @selected_objects)
		if @align_mode==0  
			if @selectedge || (@element and @element.is_a?Sketchup::ConstructionLine) || @select_axis
				@align_mode=1 
			elsif @selectface.is_a?Sketchup::Face
				@align_mode=2 
			end 
		end    
		if @align_mode==1
			if @selectedge 
				if @selectedge1  
					if @selected_objects && (not @element2.include? @selected_objects)
						@element2<< @selected_objects
						@selectedge2<< @selectedge
						@pedge2 << @selectedge 
					elsif (@selectedge2.empty? || (@mulsel==1 && @selecting)) && @element && ((@element.is_a?Sketchup::Group) || (@element.is_a?Sketchup::ComponentInstance) || (@element.is_a?Sketchup::Edge))
					if @element2.include? @element
						index=@element2.index(@element)
						@element2.delete_at(index)
						@selectedge2.delete_at(index)
						@pedge2.delete_at(index)
					else
						@element2<< @element
						@selectedge2<< @selectedge
						@pedge2 << @selectedge
					end
					end
				else
					@e1=[] ;@e2=[]
					@selectedge1=@selectedge
				end
			else
				@align_mode==0 
			end
		elsif @align_mode==2      
			if @selectface.is_a?Sketchup::Face
				if @selectface1 
					if @selected_objects && (not @element2.include? @selected_objects)
						@element2<< @selected_objects
						@selectface2<< @selectface
						@transface2<< @trans
						@ps2<< @ps       
					elsif @selectface2.empty? || (@mulsel==1 && @selecting)
						if @element2.include? @element
							index=@element2.index(@element)
							@element2.delete_at(index)
							@selectface2.delete_at(index)
							@transface2.delete_at(index)
							@ps2.delete_at(index)
						else
							@element2<< @element
							@selectface2<< @selectface
							@transface2<< @trans
							@ps2<< @ps                       
						end
					end
				else       
					@e2=[];@e1=[];@ps1=[]
					@selectface1=@selectface
					@transface1=@trans
					@ps1=@ps
				end # end else  
			else
				@align_mode==0 
			end
		end #else 
end
def getMenu(menu)
	if !@e1.empty?      
		self.rotate_elements_edge
	elsif !@e2.empty?
		self.rotate_elements_face   
	end      
end
def normal_face(ver)
	vx=nil;vy=nil;line=nil
	(0..ver.length-2).each{|i| 
		vx=ver[i].vector_to ver[i+1]  
		line=ver[i],ver[i+1]
		break if vx.length!=0  
	}
	if line
		(0..ver.length-1).each{|i| 
			v2=ver[i].project_to_line line
			vy=v2.vector_to ver[i]
			break if vy.length!=0  
		}
		return (vx.cross vy) if vx && vy 
	end
	Sketchup.active_model.select_tool(nil) 
end
def onLButtonUp(flags, x, y, view)  
	if  @align_mode==1 && @selectedge1 
		if @selected_edges.empty?
			self.align_edgeonedge if (not @selectedge2.empty?) && @mulsel==0
		else
			@selected_edges.each{|e|
				@selectedge2<< e
				@element2<< e
				@pedge2<< [e.start.position,e.end.position] 
			}
			self.align_edgeonedge if !@selected_objects
			view.model.selection.clear
			@selected_edges.clear
			@selected_faces.clear
		end
	elsif @align_mode==2 && @selectface1 
		if @selected_faces.empty?
			self.align_faceonface if (not @selectface2.empty?) && @mulsel==0
		else
		tedit=view.model.edit_transform
		@selected_faces.each{|e|
			@selectface2<< e
			@element2<< e
			ps=(e.outer_loop.vertices.collect{|p| p.position.transform tedit } )          
			ps<< ps[0]
			@ps2<< ps
			@transface2<< tedit
		}
		self.align_faceonface if !@selected_objects
		view.model.selection.clear
		@selected_edges.clear
		@selected_faces.clear
		end
	end
	self.setstatus(view) 
end
def rotate_elements_face
	model = Sketchup.active_model
	entities = model.active_entities 
	model.start_operation @strings.GetString("Align Faces-Opposite"),true,false,false   
	(0..@e2.length-1).each{|i|
		e=@e2[i];cen=@center[i]
		if e.valid? && (e.is_a?Sketchup::Group or e.is_a?Sketchup::ComponentInstance) && cen && @zaxis
			new_transform = Geom::Transformation.new(cen,@zaxis)     
			t=e.transformation    
			g=entities.add_group e
			g.transformation=new_transform
			e.transformation=new_transform.inverse*t  
			t=Geom::Transformation.new(cen,@zaxis.reverse) 
			g.transformation=t
			g.explode  
		end
	}
	model.commit_operation 
end
def rotate_elements_edge
	model = Sketchup.active_model
	entities = model.active_entities 
	model.start_operation @strings.GetString("Align Egdes-Opposite"),true,false,false
	(0..@e1.length-1).each{|i|
		e=@e1[i];cen=@center[i]
		if e.valid? && (e.is_a?Sketchup::Group or e.is_a?Sketchup::ComponentInstance) && cen && @zaxis
			new_transform = Geom::Transformation.new(cen,@zaxis)     
			t=e.transformation    
			g=entities.add_group e
			g.transformation=new_transform
			e.transformation=new_transform.inverse*t  
			t=Geom::Transformation.new(cen,@zaxis.reverse) 
			g.transformation=t
			g.explode 
		end
	}
	model.commit_operation 
end
def onKeyDown(key, repeat, flags, view)
	if (key == VK_CONTROL) or ((RUBY_PLATFORM.include?('darwin')) and (key==VK_ALT))
		@ctrl_down_time=true
		@selecting=true
	end
end
def onKeyUp(key, repeat, flags, view)
	case key
	when VK_CONTROL
		@ctrl_down_time=nil
		@selecting=false
	when VK_ALT
		if (RUBY_PLATFORM.include?('darwin'))
			@ctrl_down_time=nil
			@selecting=false
		end
	when 13
		self.align_edgeonedge if @selectedge1 and @selectedge2.length>0 and @align_mode==1 and @mulsel == 1
		self.align_faceonface if @selectface1 and @selectface2.length>0 and @align_mode==2 and @mulsel == 1
		self.setstatus(view) 
	when VK_UP,VK_DOWN   
		if not @e1.empty?      
			self.rotate_elements_edge
		elsif not @e2.empty?                        
			self.rotate_elements_face   
		end  
	when 9
		@alignfix=1-@alignfix
	when 15,48,VK_LEFT,VK_RIGHT 
		@alignfix=1-@alignfix if RUBY_PLATFORM.include?('darwin')	
	end
	self.setstatus(view)
end
def align_edgeonedge
	model = Sketchup.active_model
	entities = model.active_entities   
	model.start_operation @strings.GetString("Align Edge"),true,false,false
	@e1=[]
	cp1start=@selectedge1[0]
	cp1end=@selectedge1[1]
	v1=cp1start.vector_to cp1end 
	line = [cp1start,cp1end]
	midedge1=Geom::Point3d.new( (cp1start.x+cp1end.x)/ 2,(cp1start.y+cp1end.y)/ 2,(cp1start.z+cp1end.z)/ 2)
	@e1=@element2;@center=[];@sub_entities=[];@vector_array=[]
	@zaxis=v1
	(0..@selectedge2.length-1).each{ |i|
		pe2=@pedge2[i]
		element2=@element2[i]  
		selectedge2=@selectedge2[i]
		cp2start=pe2[0]
		cp2end=pe2[1]
		if not element2.is_a?( Sketchup::Edge)  
			midedge2=Geom::Point3d.new( (cp2start.x+cp2end.x)/ 2,(cp2start.y+cp2end.y)/ 2,(cp2start.z+cp2end.z)/ 2)
			v2=cp2start.vector_to cp2end 
			new_transform2 = Geom::Transformation.new(midedge2,v2)             
			tmpgrp=entities.add_group   
			t=element2.transformation     
			g=tmpgrp.entities.add_group element2
			g.transformation=new_transform2
			element2.transformation=new_transform2.inverse*t          
			pointonline1 = cp2start.project_to_line line
			pointonline2 = cp2end.project_to_line line
			midonline=midedge2.project_to_line line     
			v3= (pointonline1==pointonline2) ? v1 : (pointonline1.vector_to pointonline2)
			new_transform3 = (@alignfix==1) ?  Geom::Transformation.new(midedge1,v3) : Geom::Transformation.new(midonline,v3)   
			@center[i]=new_transform3.origin
			g.transformation=new_transform3  
			g.explode
		else
			pointonline1 = cp2start.project_to_line line
			pointonline2 = cp2end.project_to_line line 
			v1=nil,v2=nil
			if @alignfix==1
				v1 = cp2start.vector_to cp1start          
				v2 = cp2end.vector_to cp1end          
			else
				v1 = cp2start.vector_to pointonline1
				v2 = cp2end.vector_to pointonline2
			end
			if not @sub_entities.include?(element2.vertices[0])
				@sub_entities << element2.vertices[0]
				@vector_array << v1
			end
			if not @sub_entities.include?(element2.vertices[1])
				@sub_entities << element2.vertices[1]
				@vector_array << v2
			end
		end
	}
	entities.transform_by_vectors @sub_entities, @vector_array           
	self.reset
	model.commit_operation
end
def align_faceonface 
	model = Sketchup.active_model
	entities = model.active_entities   
	model.start_operation @strings.GetString("Align Faces"),true,false,false 
	@e2=[]
	@zaxis=self.normal_face(@ps1)
	cp1=self.centerface(@selectface1)
	cp1.transform! @transface1
	new_transform1 = Geom::Transformation.new(cp1,@zaxis)   
	plane = [cp1,@zaxis]
	@e2=@element2;@center=[]  ;@sub_entities=[]; @vector_array=[]
	(0..@selectface2.length-1).each{ |i|
		element2=@element2[i]
		selectface2=@selectface2[i]
		transface2=@transface2[i]
		ps2=@ps2[i]
		if selectface2.is_a?Sketchup::Face 
			if not element2.is_a?Sketchup::Face
				cp2=self.centerface(selectface2)
				cp2.transform! transface2
				v2= self.normal_face(ps2)
				normalface2=selectface2.normal
				normalface2.transform! transface2
				v2.reverse! if (v2.angle_between normalface2)>= Math::PI/ 2
				new_transform2 = Geom::Transformation.new(cp2,v2)     
				tmpgrp=entities.add_group
				t=element2.transformation
				g=tmpgrp.entities.add_group element2
				g.transformation=new_transform2
				element2.transformation=new_transform2.inverse*t           
				pointonplane = cp2.project_to_plane plane         
				new_transform3 = @alignfix==1 ? Geom::Transformation.new(cp1,@zaxis) : Geom::Transformation.new(pointonplane,@zaxis) 
				@center[i] =new_transform3.origin
				g.transformation=new_transform3
				g.explode
			else
				curves=[]
				selectface2.edges.each{|e| curves<<e.curve if (e.is_a?Sketchup::Edge and e.curve and (not curves.include?(e.curve))) }
				curves.each{|c| c.first_edge.explode_curve }
				selectface2.vertices.each{|ver2|
					p=ver2.position
					p1 = p.project_to_plane plane
					v=p.vector_to p1
					if not @sub_entities.include?(ver2)
						@sub_entities << ver2 
						@vector_array << v
					end
				}
			end
		end         
	}
	entities.transform_by_vectors @sub_entities, @vector_array 
	self.reset
	model.commit_operation
end
def onCancel(reason, view)
	self.reset(view)
end
def reset(view=nil)
	@align_mode=0
	@mulsel=0
	@ctrl_down_time=nil
	@selectface1=nil
	@selectface2=[]
	@element1=nil
	@element2=[]
	@selectface=nil
	@trans=nil
	@transface1=nil
	@transface2=[]
	@ps2=[];@ps1=[]
	@selectedge1=nil
	@selectedge2=[]
	@selectedge=nil
	@pedge2=[]        
	self.setstatus(view)
end
def centerface(fa)
	cp=Geom::Point3d.new
	if fa.is_a?Sketchup::Face
		numver=fa.outer_loop.vertices.length
		fa.outer_loop.vertices.each{|v|
		cp.x=cp.x+v.position.x
		cp.y=cp.y+v.position.y
		cp.z=cp.z+v.position.z
	}
	cp.x=cp.x/ numver
	cp.y=cp.y/ numver
	cp.z=cp.z/ numver      
	end
	return cp
end
def resume(view)
	self.setstatus(view)
end
end
def self.aligntool  
	Sketchup.active_model.select_tool AlignTool.new
end
end
end
#S4U::S4u_AlignTool.aligntool
