=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Copyright © 2009 Fredo6 - Designed and written April 2009 by Fredo6

# Permission to use this software for any purpose and without fee is hereby granted
# Distribution of this software for commercial purpose is subject to:
#  - the expressed, written consent of the author
#  - the inclusion of the present copyright notice in all copies.

# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#-----------------------------------------------------------------------------
# Name			:   RoundCorner_AlgoRound.rb
# Original Date	:   30 Jun 2009 - version 2.0
# Description	:   Algorithm for RoundCorner in Round mode
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module RoundCorner
							  
class RoundCornerAlgo

#Compute a round corner with 3 edges
def corner_compute_3round(vd)
	leds = vd.leds

	#Using the golden edge if any and computing the golden section
	ed0 = vd.gold_ed
	icorner0 = (ed0.corners[0] == vd) ? 0 : 1
	section0 = compute_sections_multi ed0, icorner0
	
	#Computing the other edges
	iA = (ed0.facemain == ed0.lfaces[0]) ? 0 : 1
	iB = 1 - iA
	pairA = ed0.pairs[2 * icorner0 + iA]
	pairB = ed0.pairs[2 * icorner0 + iB]
	ed1 = pairA.leds.find { |ed| ed != ed0 && ed_common_face?(ed0, ed0.facemain, ed)}
	ed2 = pairB.leds.find { |ed| ed != ed0 && ed != ed1}
	pair_other = vd.pairs.find { |pair| !pair.leds.include?(ed0) }
		
	#Computing the grid
	n0 = section0.length - 1
	origin = pair_other.ptcross	
	if pair_other.rounding
		lorigin = pair_other.rounding.reverse
	else
		lorigin = []
		for i in 0..n0
			lorigin[i] = origin
		end	
	end
	vec0 = ed0.vec
	vec0 = vec0.reverse if icorner0 == 1 
	normal = ed1.vec * ed2.vec
	vec = vd.vertex.position.vector_to origin
	lpts = []
	profile = (ed1.round_profile || ed2.round_profile) ? ['C', @num_seg] : @profile_type
	for i in 0..n0
		lpt = profile_compute_by_vectors profile, section0[i], vec0, lorigin[i], normal
		lpts.push lpt
	end
	icorner1 = (ed1.corners[0] == vd) ? 0 : 1
	icorner2 = (ed2.corners[0] == vd) ? 0 : 1
	
	section1 = lpts.first
	section1 = section1.reverse unless section1[0].on_plane?(ed1.facemain.plane)
	
	section2 = lpts.last
	section2 = section2.reverse unless section2[0].on_plane?(ed2.facemain.plane)
	
	ed1.cross_sections[icorner1] = section1 #unless ed1.cross_sections[icorner1]
	ed2.cross_sections[icorner2] = section2 #unless ed2.cross_sections[icorner2]
	
	#Constructing the vmesh
	vmesh = []
	lquads = []
	n = lpts.length - 2
	for i in 0..n
		pts1 = lpts[i]
		pts2 = lpts[i+1]
		m = pts1.length - 2
		for j in 0..m
			if pts1[j+1] == pts2[j+1]
				pts = [pts1[j], pts1[j+1], pts2[j]]
			else
				pts = [pts1[j], pts1[j+1], pts2[j+1], pts2[j]]
			end	
			lquads.push pts
		end
	end	
	
	nb = @num_seg
	n0 = nb / 2
	if (nb / 2) * 2 == nb
		odd = 0
	else
		odd = 1
	end	
	
	#Triangle corner
	if (ed1.facemain.normal.parallel?(ed0.facemain.normal))
		n1 = n0 - 1
		face1 = (ed1.lfaces[0] == ed1.facemain) ? ed1.lfaces[1] : ed1.lfaces[0]
	else	
		n1 = n0 - 1 + odd
		face1 = ed1.facemain
	end	
	lq1 = []
	for i in 0..nb-1
		for j in 0..n1
			lq1.push lquads[i * nb + j]
		end
	end	
	if lq1.length > 0
		quad = lquads[0]
		normalref = compute_normal_reference(ed1, face1, quad[0], quad[1])
		vmesh.push [lq1, face1, normalref]
	end	

	#Side corner 1
	lq2 = []
	for i in 0..n0-1+odd
		for j in n1+1..nb-1
			lq2.push lquads[i * nb + j]
		end
	end	
	if lq2.length > 0 #&& n1 >= 0
		quad = lquads[n1+1]
		normalref = compute_normal_reference(ed1, ed0.facemain, quad[1], quad[0])
		vmesh.push [lq2, ed0.facemain, normalref]
	end	

	#Side corner 2
	lq3 = []
	for i in n0+odd..nb-1
		for j in n1+1..nb-1
			lq3.push lquads[i * nb + j]
		end
	end	
	if ed2.facemain.normal.parallel?(face1.normal)		
		face2 = (ed2.lfaces[0] == ed2.facemain) ? ed2.lfaces[1] : ed2.lfaces[0]
	else	
		face2 = (ed2.lfaces[0] == ed2.facemain) ? ed2.lfaces[0] : ed2.lfaces[1]
	end	
	if lq3.length > 0 #&& n1 >= 0
		quad = lquads[(n0+odd)*nb + n1+1]
		normalref = compute_normal_reference(ed2, face2, quad[1], quad[0])
		vmesh.push [lq3, face2, normalref]
	end	
	
	#Storing the vmesh
	vd.vmesh = vmesh
end

#---------------------------------------------------------------------------------------------------------------------------
# Corners with 4 edges
#---------------------------------------------------------------------------------------------------------------------------

#Compute round corner for termination with 4 edges
def corner_compute_round_4(vd)	
	#Computing the cross sections for each edge
	leds = vd.leds
	leds.each do |ed|
		icorner0 = (ed.corners[0] == vd) ? 0 : 1
		compute_sections_multi ed, icorner0	
	end	
	
	#picking one edge
	ed0 = leds[0]
	
	#Looking for adjacent edges
	oface0 = (ed0.facemain == ed0.lfaces[0]) ? ed0.lfaces[1] : ed0.lfaces[0]
	ed1 = leds.find { |ed| ed != ed0 && ed_common_face?(ed0, ed0.facemain, ed)}
	ed2 = leds.find { |ed| ed != ed0 && ed_common_face?(ed0, ed0.facemain, ed)}
	ed2 = leds.find { |ed| ed != ed0 && ed != ed1 && ed_common_face?(ed0, oface0, ed)}
	ed4 = vd.leds.find { |ed| ed != ed0 && ed != ed1 && ed != ed2 }

	#Orientation of the edges
	icorner0 = (ed0.corners[0] == vd) ? 0 : 1
	icorner1 = (ed1.corners[0] == vd) ? 0 : 1
	icorner2 = (ed2.corners[0] == vd) ? 0 : 1
	icorner4 = (ed4.corners[0] == vd) ? 0 : 1

	xsection0 = ed0.cross_sections[icorner0]
	xsection1 = ed1.cross_sections[icorner1]
	xsection2 = ed2.cross_sections[icorner2]
	xsection4 = ed4.cross_sections[icorner4]	
	
	if xsection0.include?(xsection1[0])
		xfirst = xsection0
		xlast = xsection4
	else	
		xlast = xsection0
		xfirst = xsection4
	end
	
	xfirst = xfirst.reverse if xsection1.include?(xfirst[0])
	xlast = xlast.reverse if xsection1.include?(xlast[0])
	xsection2 = xsection2.reverse if xsection2[0].distance(xsection1.first) > xsection2[0].distance(xsection1.last) &&
	                                 xsection2.last.distance(xsection1.first) < xsection2.last.distance(xsection1.last)
			
	#creating the grid sections
	profile0 = (ed0.round_profile) ? ['C', @num_seg] : @profile_type
	vec1 = vd.vertex.position.vector_to ed1.ptmid
	vec2 = vd.vertex.position.vector_to ed2.ptmid
	
	lpts = [xfirst]
	n = xsection1.length - 2
	n1 = n / 2
	for i in 1..n1
		norm = (xsection1[i].vector_to xsection1[i+1]) * vec1
		sec = profile_compute_by_vectors(profile0, xsection2[i], vec2, xsection1[i], norm)
		lpts.push sec.reverse
	end
	for i in n1+1..n
		norm = (xsection2[i].vector_to xsection2[i+1]) * vec2
		lpts.push profile_compute_by_vectors(profile0, xsection1[i], vec1, xsection2[i], norm)
	end
	lpts.push xlast

	#Constructing the vmesh
	vmesh = []
	lquads = []
	n = lpts.length - 2
	for i in 0..n
		pts1 = lpts[i]
		pts2 = lpts[i+1]
		m = pts1.length - 2
		for j in 0..m
			if pts1[j+1] == pts2[j+1]
				pts = [pts1[j], pts1[j+1], pts2[j]]
			else
				pts = [pts1[j], pts1[j+1], pts2[j+1], pts2[j]]
			end	
			lquads.push pts
		end
	end	
	
	#List of faces	
	nb = @num_seg
	n0 = nb / 2
	odd = ((nb / 2) * 2 == nb) ? 0 : 1

	#First quadrant
	quad = lquads[0]
	pt = quad[0]
	ll = locate_point_face_4 pt, ed0, ed1, ed2, ed4
	faceA = ll[0]
	edA = ll[1]
	edB = ll[2]
	lq = []
	ni = (faceA.normal.parallel?(edB.facemain.normal)) ? n0 + odd : n0
	nj = (faceA == edA.facemain) ? n0 + odd : n0
	for i in 0..ni-1
		for j in 0..nj-1
			lq.push lquads[i * nb + j]
		end
	end	
	if lq.length > 0
		normalref = compute_normal_reference(edA, faceA, quad[0], quad[1])
		vmesh.push [lq, faceA, normalref]
	end	

	#second quadrant
	quad = lquads[nb-1]
	pt = quad[1]
	ll = locate_point_face_4 pt, ed0, ed1, ed2, ed4
	faceA = ll[0]
	edA = ll[1]
	edB = ll[2]
	lq = []
	ni2 = (faceA.normal.parallel?(edB.facemain.normal)) ? n0 + odd : n0
	for i in 0..ni2-1
		for j in nj..nb-1
			lq.push lquads[i * nb + j]
		end
	end	
	if lq.length > 0
		normalref = compute_normal_reference(edA, faceA, quad[1], quad[0])
		vmesh.push [lq, faceA, normalref]
	end	

	#Third quadrant
	quad = lquads[nb * (nb-1)]
	pt = quad[3]
	ll = locate_point_face_4 pt, ed0, ed1, ed2, ed4
	faceA = ll[0]
	edA = ll[1]
	edB = ll[2]
	lq = []
	nj = (faceA == edA.facemain) ? n0 + odd : n0
	for i in ni..nb-1
		for j in 0..nj-1
			lq.push lquads[i * nb + j]
		end
	end	
	if lq.length > 0
		normalref = compute_normal_reference(edA, faceA, quad[0], quad[1])
		vmesh.push [lq, faceA, normalref]
	end	
	
	#Fourth quadrant
	quad = lquads[nb * nb - 1]
	pt = quad[2]
	ll = locate_point_face_4 pt, ed0, ed1, ed2, ed4
	faceA = ll[0]
	edA = ll[1]
	edB = ll[2]
	lq = []
	for i in ni2..nb-1
		for j in nj..nb-1
			lq.push lquads[i * nb + j]
		end
	end	
	if lq.length > 0
		normalref = compute_normal_reference(edA, faceA, quad[1], quad[0])
		vmesh.push [lq, faceA, normalref]
	end	
		
	#Storing the vmesh
	vd.vmesh = vmesh
end

#Locate on which edge is the given point
def locate_point_face_4(pt, ed0, ed1, ed2, ed4)
	[[ed0, ed1], [ed0, ed2], [ed4, ed1], [ed4, ed2]].each do |ll|
		face = which_face_4(pt, ll[0], ll[1])
		return [face] + ll if face
	end
	nil
end

#Determine on which face is a given point
def which_face_4(pt, eda, edb)
	plane = [eda.ptmid, eda.vec * edb.vec]
	return nil unless pt.on_plane?(plane)
	eda.lfaces.each do |face|
		return face if ed_common_face?(eda, face, edb)
	end
	nil	
end

#Orthogonal section to an edge
def orthogonal_section_ed(ed, vd)
	#getting the pairs and cross points
	icorner = (ed.corners[0] == vd) ? 0 : 1
	iA = (ed.facemain == ed.lfaces[0]) ? 0 : 1
	iB = 1 - iA
	pairA = ed.pairs[2 * icorner + iA]
	pairB = ed.pairs[2 * icorner + iB]
	ptA = pairA.ptcross
	ptB = pairB.ptcross
	vf1 = ed.lfaces[0].normal
	vf2 = ed.lfaces[1].normal
	vf2 = vf2.reverse unless vf1 % vf2 > 0
	vfmean = Geom.linear_combination 0.5, vf1, 0.5, vf2
	
	#compting the projection on plane
	plane = [ptA, vfmean * ptA.vector_to(ptB)]
	section = compute_profile_edge(ed)
	ed.cross_sections[icorner] = section.collect { |pt| Geom.intersect_line_plane [pt, ed.vec], plane }
end

#---------------------------------------------------------------------------------------------------------------------------
# Corners with any number of edges in Star configuration
#---------------------------------------------------------------------------------------------------------------------------

#Compute a round corner with 5 or more edges - TRiangulation de Rastapopoulos
def corner_compute_star(vd)

	#Computing the cross sections for each edge
	lsec = []
	vd.leds.each do |ed|
		lsec.push [ed, orthogonal_section_ed(ed, vd)]
	end	
	
	#Finding the pivot point and Plane that best fit cross points
	nb = @num_seg / 2 + 1
	lptcross = lsec.collect { |ll| ll[1][nb] }
	plane = Geom.fit_plane_to_points lptcross
	vpos = vd.vertex.position
	ptproj = vpos.project_to_plane plane
	normal = ptproj.vector_to vpos
	fac = 0.5
	ptpivot = Geom.linear_combination fac, vpos, 1.0 - fac, ptproj
	@lst_mark_points.push ptpivot
		
	#Creating the mesh
	vmesh = []
	profile0 = ['C', nb]
	lsec.each do |ll|
		ed = ll[0]
		xsection = ll[1]
		lpts = xsection.collect do |pt| 
			vec = vpos.vector_to(pt)
			profile_compute_by_vectors(profile0, pt, vec, ptpivot, normal)
		end	
		lquads = []
		n = lpts.length - 2
		for i in 0..n
			pts1 = lpts[i]
			pts2 = lpts[i+1]
			m = pts1.length - 2
			for j in 0..m
				if pts1[j+1] == pts2[j+1]
					pts = [pts1[j], pts1[j+1], pts2[j]]
				else
					pts = [pts1[j], pts1[j+1], pts2[j+1], pts2[j]]
				end	
				lquads.push pts
			end
		end	
		
		#Separating the vmesh i two parts
		odd = ((@num_seg / 2) * 2 == @num_seg) ? 0 : 1
		if odd == 0
			n1 = @num_seg / 2 + odd
			m1 = n1 * (nb + odd) - 1
		else
			n1 = @num_seg / 2 + odd + 1
			m1 = n1 * (nb - odd)
		end
		
		quad = lquads[0]
		faceref = ed.facemain
		normalref = compute_normal_reference(ed, faceref, quad[1], quad[0])
		vmesh.push [lquads[0..m1], faceref, normalref]
		
		quad = lquads.last
		faceref = (ed.lfaces[0] == ed.facemain) ? ed.lfaces[1] : ed.lfaces[0]
		normalref = compute_normal_reference(ed, faceref, quad[1], quad[0])
		vmesh.push [lquads[m1+1..-1], faceref, normalref] if m1 >= 0
	end
	
	vd.vmesh = vmesh
end

#Compute the normal reference at a position of the edge or corner profile.
#This is done for the right orientation of faces created
def compute_normal_reference(ed, face, pt1, pt2)
	iface = (ed.lfaces[0] == face) ? 0 : 1
	vy = ed.lvecins[iface] * face.normal
	vec = pt1.vector_to pt2
	vec * vy
end

end	#class RoundCornerAlgo

end	#End Module RoundCorner
