=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_AlgoSharp.rb
# Original Date	:   30 Jun 2009 - version 2.0
# Description	:   Algorithm for RoundCorner in Sharp mode
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module RoundCorner
							  
class RoundCornerAlgo

#Compute a sharp corner with 3 edges in standradized offset mode
def corner_compute_3sharp(vd)
	leds = vd.leds
	
	#Calculating the bissector planes
	vpos = vd.vertex.position
	lplanes = []
	vd.pairs.each do |pair|
		ed1 = pair.leds[0]
		ed2 = pair.leds[1]
		ed0 = leds.find { |ed| ed != ed1 && ed != ed2 }
		plane = G6.plane_by_3_points pair.ptcross, vpos, ed0.ptmid
		lplanes.push [ed0, plane]
	end	
		
	#Calculating the intersections of edge profiles with stop planes
	leds.each do |ed|
		cross_section = []
		section = compute_profile_edge ed
		ptmid = ed.ptmid
		vec = ptmid.vector_to vpos
		icorner = (ed.corners[0] == vd) ? 0 : 1
		ed_planes = []
		lplanes.each { |ll| ed_planes.push ll[1] if ll[0] != ed }
		section.each do |pt|
			pt1 = Geom.intersect_line_plane [pt, vec], ed_planes[0]
			pt2 = Geom.intersect_line_plane [pt, vec], ed_planes[1]
			cross_section.push((ptmid.distance(pt1) < ptmid.distance(pt2)) ? pt1 : pt2)	
		end
		cross_section = cross_section.reverse unless cross_section[0].on_plane?(ed.facemain.plane)
		
		ed.cross_sections[icorner] = cross_section
	end

	#Compute the corner triangle if the number of segment is odd
	n = @num_seg / 2
	return if n * 2 == @num_seg
	lfaces = []
	leds.each { |ed| lfaces.push ed.facemain }
	face0 = lfaces[0]
	if face0.normal.parallel?(lfaces[1].normal)
		ieds = [0, 1]
	elsif face0.normal.parallel?(lfaces[2].normal)
		ieds = [0, 2]
	else
		ieds = [1, 2]
	end	
	lpt = []
	ed1 = leds[ieds[0]]
	icorner1 = (ed1.corners[0] == vd) ? 0 : 1
	ed2 = leds[ieds[1]]
	icorner2 = (ed2.corners[0] == vd) ? 0 : 1
	
	pt1 = ed1.cross_sections[icorner1][n]
	pt2 = ed1.cross_sections[icorner1][n+1]
	pt3 = ed2.cross_sections[icorner2][n+1]
	pt3 = ed2.cross_sections[icorner2][n] if pt3 == pt1 || pt3 == pt2
	
	lpt.push pt1, pt2, pt3
	faceref = ed1.facemain
	normalref = compute_normal_reference(ed1, faceref, pt1, pt2)
	vd.vmesh = [[[lpt], faceref, normalref]]
end

#Compute a sharp corner with any number of edges
def corner_compute_any_sharp(vd)
	leds = vd.leds
	
	#initialization
	hshcross = {}
	leds.each { |ed| hshcross[ed.object_id] = [] }
	
	#Calculating the sections
	leds.each { |ed| intersection_any_sharp vd, ed }
	
	#Filtering the edge termination
	linter = {}
	vd.sharp_inter.each do |inter|
		pt = inter[0]
		ed0 = inter[1]
		i0 = inter[2]
		icorner0 = (ed0.corners[0] == vd) ? 0 : 1
		nsection0 = ed0.nsection
		ptold = ed0.cross_sections[icorner0][i0]
		next if ptold && nsection0[i0].distance(pt) > nsection0[i0].distance(ptold)
		ed0.cross_sections[icorner0][i0] = pt
		inter[5] = 0
		hshcross[ed0.object_id][i0] = [inter] 
		linter[ed0.object_id.to_s + ' ' + i0.to_s] = inter
	end
	
	#Assigning the intermediate points
	linter.each do |key, inter|
		pt = inter[0]
		ed1 = inter[3]
		j1 = inter[4]
		icorner1 = (ed1.corners[0] == vd) ? 0 : 1
		ptcross = ed1.cross_sections[icorner1][j1]
		next if pt == ptcross
		inter = inter.collect { |a| a }
		line = [ptcross, ptcross.vector_to(ed1.cross_sections[icorner1][j1+1])]
		
		origin = ed1.cross_sections[icorner1][j1]
		ptmid = ed1.cross_sections[icorner1][j1+1]
		vec0 = ptmid.vector_to origin
		
		inter[5] = vec0.angle_between ptmid.vector_to(pt)
		
		hsh = hshcross[ed1.object_id][j1]
		hsh.push inter
		hsh.sort! { |intera, interb| intera[5] <=> interb[5] }
		ed1.sharp_sections[icorner1][j1] = hsh.collect { |inter| inter[0] }
	end
	
	#Identifying holes
	hsh_edplanes = {}
	leds.each do |ed|
		lcross = hshcross[ed.object_id]
		n0 = lcross.length - 2
		for i in 0..n0
			linter = lcross[i] + [lcross[i+1][0]]
			nl = linter.length - 2
			for j in 0..nl
				inter1 = linter[j]
				inter2 = linter[j+1]
				lled = []
				edc = nil
				[inter1[1], inter1[3], inter2[1], inter2[3]].each do |edd| 
					if lled.include?(edd) 
						edc = edd
					else	
						lled.push edd
					end	
				end	
				if lled.length > 2
					@lst_mark_points.push inter1[0], inter2[0]
					pt = Geom.linear_combination 0.5, edc.nsection[i], 0.5, edc.nsection[i+1]
					plane = [pt, edc.nsection[i].vector_to(edc.nsection[i+1]) * edc.vec]
					key = edc.object_id.to_s + ' ' + i.to_s + ' ' + j.to_s
					hsh_edplanes[key] = [key, edc, i, plane, inter1[0], inter2[0]]
					@lst_mark_points.push pt
				end	
			end
		end
	end
	
	#Finding the plane intersections
	lst_loops = sharp_find_loops hsh_edplanes
	lst_loops.each do |loop|
		for i in 0..loop.length-1
			lst_insert = sharp_intersect_planes vd, loop
			if lst_insert 
				lst_insert.each { |ll| sharp_insert_in_section vd, ll[0], ll[1], ll[2] }
				break
			end
		end	
	end	
	
end

#Compute the section for an edge in Sharp mode at a vertex
def intersection_any_sharp(vd, ed0)
	nsection0 = compute_profile_edge ed0
	n0 = nsection0.length - 1
	vec0 = ed0.vec
	vd.leds.each do |ed1|
		next if ed1 == ed0
		vec1 = ed1.vec
		nsection1 = compute_profile_edge ed1
		for i0 in 0..n0
			pt0 = nsection0[i0]
			line0 = [pt0, vec0]
			for j1 in 0..n0-1
				pt = intersect_between_two_lines line0, vec1, nsection1[j1], nsection1[j1+1]
				vd.sharp_inter.push [pt, ed0, i0, ed1, j1] if pt
			end
		end	
	end
end

#determine if a line cut a latte (defined by 2 points and a vector)
def intersect_between_two_lines(line0, vec1, pt1A, pt1B)
	plane1 = [pt1A, pt1A.vector_to(pt1B) * vec1]
	pt = Geom.intersect_line_plane line0, plane1
	return nil unless pt
	ptprojA = pt.project_to_line [pt1A, vec1]
	ptprojB = pt.project_to_line [pt1B, vec1]
	vA = pt.vector_to ptprojA
	return pt if vA.length == 0
	vB = pt.vector_to ptprojB
	return pt if vB.length == 0
	(vA % vB <= 0) ? pt : nil
end

#Identify the loops in the holes
def sharp_find_loops(hsh_edplanes)
	lplanes = hsh_edplanes.values
	
	#Loop on edges to det
	hshed = {}
	lst_loops = []
	loop = []
	
	while true
		#Resuming or continuing at current loop end, or initiating a new loop
		if (loop.length > 0)
			edp0 = loop.last[1]
			ptend = (edp0[5] == loop.last[0]) ? edp0[4] : edp0[5]
		else
			edp0 = lplanes.find { |edp| !hshed[edp[0]]}
			break unless edp0
			ptend = edp0[5]
			loop = [[edp0[4], edp0]]	
		end
		hshed[edp0[0]] = 1
		
		#getting new segment
		edp1 = lplanes.find { |edp| edp[0] != edp0[0] && hshed[edp[0]] != -1 && (edp[4] == ptend || edp[5] == ptend) }
		break unless edp1
		
		#Checking if segment was already met
		loop.each_with_index do |ll, i|
			if ptend == ll[0]
				newloop = loop[i..-1].collect { |a| a[1] }
				newloop.each { |edp| hshed[edp[0]] = -1 }
				lst_loops.push newloop
				loop[i..-1] = []
				edp1 = nil
			end	
		end
		
		#New segment in loop
		loop.push [ptend, edp1] if edp1
		
	end
	lst_loops.push(loop.collect { |a| a[1] }) if loop.length > 0	
	lst_loops
end

#Determine the intersections of planes for sharp corners
#Progressive algorithm, based on heuristics
def sharp_intersect_planes(vd, lplanes)	
	
	#Sorting the planes by inclinations
	#lplanes = sharp_sort_planes_by_inclination vd, lplanes
	
	#Algorithm to explore intersections of planes by group of 3
	n = lplanes.length
	for i0 in 0..n-1
		lst_insert = []
		lst_pt = []
		for i in i0..n-1+i0
			icur = i0.modulo(n)
			inext = (i + 1).modulo(n)
			iprev = (i + 2).modulo(n)
			lst_3planes = [lplanes[icur], lplanes[inext], lplanes[iprev]]
			ptinter = sharp_compute_intersect_planes vd, lplanes, lst_3planes
			next unless ptinter
			lst_pt.push ptinter
			lst_3planes.each { |edp| lst_insert.push [edp[1], edp[2], ptinter] }
			if lst_pt.length == n - 2
				lst_pt.each { |pt| @lst_mark_points.push pt }
				return lst_insert
			end	
		end	
	end
	nil
end

# Determine the intersection point between 3 planes, and check if valid
def sharp_compute_intersect_planes(vd, all_planes, lst_3planes)
	#Computing the intersection of the 3 planes
	planes = lst_3planes.collect { |edp| edp[3] }
	line = Geom.intersect_plane_plane planes[0], planes[1]
	return nil unless line
	ptinter = Geom.intersect_line_plane line, planes[2]
	return nil unless ptinter
	return ptinter if all_planes.length == 3
	
	#List of all other planes	
	otherplanes = all_planes.find_all { |p| !lst_3planes.include?(p) }
	
	#Check if point is not 'above' otherplane
	vpos = vd.vertex.position
	otherplanes.each do |edp|
		plane = edp[3]
		next if ptinter.on_plane?(plane)
		vdproj = vpos.project_to_plane plane
		ptproj = ptinter.project_to_plane plane
		if vpos.vector_to(vdproj) % ptinter.vector_to(ptproj) > 0
			#puts "above"
			return nil
		end	
	end
	ptinter
end

#Insert an intermediate point for the latte
def sharp_insert_in_section(vd, ed, j, ptinter)
	icorner = (ed.corners[0] == vd) ? 0 : 1
	sharp_section = ed.sharp_sections[icorner][j]
	
	#No point already in the intermediate section
	unless sharp_section && sharp_section.length > 0
		ed.sharp_sections[icorner][j] = [ptinter]
		return
	end
	
	#Inserting the point in the right place
	ptmid = ed.cross_sections[icorner][j+1]
	vec0 = ptmid.vector_to ed.cross_sections[icorner][j]
	angle0 = vec0.angle_between ptmid.vector_to(ptinter)
	sharp_section.each_with_index do |pt, i|
		return if pt == ptinter 
		angle = vec0.angle_between ptmid.vector_to(pt)
		if angle > angle0
			if i == 0
				ed.sharp_sections[icorner][j] = [ptinter] + sharp_section
			else
				ed.sharp_sections[icorner][j] = sharp_section[0..i-1] + [ptinter] + sharp_section[i..-1]
			end	
			return
		end	
	end
	ed.sharp_sections[icorner][j].push ptinter
end

#Sort planes by inclination
def sharp_sort_planes_by_inclination(vd, lplanes)
	
	#Barycenter of edges
	lpt = lplanes.collect { |edp| Geom.linear_combination 0.5, edp[4], 0.5, edp[5] }
	bary = G6.straight_barycenter lpt
	@lst_mark_points.push bary, vd.vertex.position
	
	vec = bary.vector_to vd.vertex.position
	pi2 = Math::PI * 0.5
	angle0 = pi2
	i0 = 0
	lplanes.each_with_index do |edp, i| 
		v = edp[3][1]
		angle = (pi2 - vec.angle_between(v)).abs
		if angle <= angle0
			i0 = i
			angle0 = angle
		end	
	end
	
	
	#Rearranging the planes
	if i0 > 0
		lplanes = lplanes[i0..-1] + lplanes[0..i0-1]
	end
	
	lplanes
end

end	#class RoundCornerAlgo

end	#End Module RoundCorner
