=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Copyright 2010 - Designed September 2010 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			:   CurviloftAlong.rb
# Original Date	:   4 Jan 2010 - version 1.0
# Description	:   Specific method for AlongPath methods in the Curviloft script family
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_Curviloft

#Describe the rail information for a link
CVL__LOFT_RailParam = Struct.new :full_rail, :part_rail, :full_rail_len, :part_rail_len, :seg_info1, :seg_info2

#Rail and Contour proximity information
CVL__LOFT_RailProximity = Struct.new :rpt, :rseg, :ppt, :pseg, :rdist, :contact, :dcontact, :iplate, :plate,
                                     :pts_rail, :loop_rail, :pts_plate, :extra_seg_beg, :extra_seg_end

class CVL_LoftAlgo 

#----------------------------------------------------------------------------
# ALONG: analysis of Contours
#----------------------------------------------------------------------------

#Verify contours for Loft Along
def along_check_contours(lst_contours, from_selection=false)

	#Handle only the rail is defined
	nb_contours = lst_contours.length
	return false if nb_contours == 1
	
	#Computing the plates
	lplates = lst_contours.collect { |lpt| plate_create(lpt) }
	
	#Testings rails
	along_compute_rail lplates, from_selection

	#Creating the borders for highlight
	@lst_links.each do |link|
		link.hiborders = link.plates.collect { |plate| plate.pts + ((plate.loop) ? [plate.pts[0]] : []) }
	end
		
	true
end

#Return the final ordering of plates
def along_get_order
	@lorder
end

#Calculate the rail information for a given contour irail, vis-a-vis the other contours
def along_compute_rail(lplates, from_selection=false)
	nb_plates = lplates.length - 1
	
	#Checking if there is a single open curve
	ls = []
	lplates.each_with_index do |plate, iplate|
		ls.push [plate.loop, iplate]
	end
	lsingle = ls.find_all { |a| !a[0] }
	if from_selection
		range = 0..0
	elsif lsingle.length == 1
		i = lsingle[0][1]
		range = i..i
	else
		range = 0..nb_plates
	end
	
	#Computing the intersection of each contours with others
	all_crossings = []
	for irail in range
		plate0 = lplates[irail]
		pts_rail = plate0.pts
		loop_rail = plate0.loop
		if loop_rail
			pts_rail += [pts_rail[0]]
		else
			extra_seg_beg = along_extra_segment_beg pts_rail
			extra_seg_end = along_extra_segment_end pts_rail
		end	
		nr = pts_rail.length - 1
		
		#Computing the intersections
		lcrossings = []
		lplates.each_with_index do |plate, iplate|
			next if iplate == irail
			
			#Points for the plate
			bary = plate.bary
			pts_plate = plate.pts
			pts_plate = pts_plate + [pts_plate[0]] if plate.loop
				
			#Computing the proximity of the plate and the rail. If contact then, done
			rprox = along_proximity_create pts_rail, loop_rail, iplate, plate, pts_plate, extra_seg_beg, extra_seg_end
			along_proximity_at_contours rprox
			
			puts "rprox.contact = #{rprox.contact}"
			
			#No contact
			unless rprox.contact == 1
				#Intersection at Plane 
				if plate.quasi_plane != 0 && along_proximity_by_plane(rprox, rprox.rpt)
					puts "quasi"
				
				#Alternate distance from barycenter
				else
					dcontact = rprox.dcontact
					puts "contact #{dcontact}"
					factor = 0.5
					rprox_bary = rprox.clone
					along_proximity_at_barycenter rprox_bary
					d = rprox_bary.dcontact
					rprox = rprox_bary if d < factor * dcontact
				end
			end
			
			#Crossing information
			lcrossings.push rprox
		end
		
		#Sorting the intersections and Storing the intersection info for the main rail
		lcrossings.sort! { |a, b| (a.rseg == b.rseg) ? a.rdist <=> b.rdist : a.rseg <=> b.rseg }
		all_crossings.push [irail, pts_rail, lcrossings]		
		
	end	
	
	#Determining the best rail
	irail, original_rail, lcrossings = along_best_rail all_crossings
	loop_rail = (original_rail[0] == original_rail[-1])
	lst_plates = lcrossings.collect { |a| a.plate }
	@single_link = (lst_plates.length <= 2)
	along_adjust_proximity lcrossings, original_rail
	
	#Recording the right order of plates (for rollback)
	@lorder = [irail] + lcrossings.collect { |cross| cross.iplate }
	
	#Only one plate - Generate the plate extremities
	duplicated = false
	noedit = false
	if lst_plates.length == 1
		@lorder = [irail, lcrossings[0].iplate]
		lst_plates = along_generate_all_single_links lcrossings, original_rail
		@lst_plates = lst_plates
		return
	end
	
	#Adding the closure if the rail is in loop
	lcrossings += [lcrossings[0]] if loop_rail

	#Computing the rail and creating the links
	native_rail_offset_info = G6.offset3d_info original_rail
	for i in 0..lcrossings.length-2
		#Initialization
		rprox1 = lcrossings[i]
		rprox2 = lcrossings[i+1]
		
		plate1 = rprox1.plate
		iseg1 = rprox1.rseg
		pt1 = rprox1.rpt
		pseg1 = rprox1.pseg
		ppt1 = rprox1.ppt
		
		plate2 = rprox2.plate
		iseg2 = rprox2.rseg
		pt2 = rprox2.rpt
		pseg2 = rprox2.pseg
		ppt2 = rprox2.ppt
		
		full_rail = original_rail
		rail_offset_info = native_rail_offset_info
		
		#Adjusting the full rail if needed (for loop configuration)
		if loop_rail && iseg1 > iseg2
			full_rail, iseg1, iseg2 = along_adjust_full_rail full_rail, iseg1, iseg2
			rail_offset_info = G6.offset3d_info full_rail
		end
		
		#Reconstructing the rail
		part_rail = along_rail_portion full_rail, pt1, pt2, iseg1, iseg2
		part_rail = full_rail[1..-1] if part_rail.length <= 1
		
		#Vectors at plate intersection
		plate1.vecrail = part_rail[0].vector_to part_rail[1] unless plate1.vecrail
		plate2.vecrail = part_rail[-2].vector_to part_rail[-1]
		
		#Creating the Rail information structure
		rail_param = F6_Curviloft::CVL__LOFT_RailParam.new
		rail_param.full_rail = full_rail
		rail_param.full_rail_len = G6.curl_length(full_rail)
		rail_param.part_rail = part_rail
		rail_param.part_rail_len = G6.curl_length(part_rail)		
		rail_param.seg_info1 = [iseg1, pt1, pseg1, ppt1]
		rail_param.seg_info2 = [iseg2, pt2, pseg2, ppt2]
		
		#Creating the link
		link = link_create plate1, plate2	
	
		link.rail_param = rail_param
		link.angle_offset = (duplicated) ? 0 : G6.offset3d_twist_angle(rail_offset_info, iseg1, iseg2)
	end	
	
	#Final step
	@all_duplicated = duplicated
	@lst_plates = lst_plates
end
 
#------------------------------------------------------------------------ 
# Calculation of Proximity and Contact Points
#------------------------------------------------------------------------ 

def along_proximity_create(pts_rail, loop_rail, iplate, plate, pts_plate, extra_seg_beg, extra_seg_end)
	rprox = CVL__LOFT_RailProximity.new
	rprox.iplate = iplate
	rprox.plate = plate
	rprox.pts_plate = pts_plate
	rprox.pts_rail = pts_rail
	rprox.loop_rail = loop_rail
	rprox.extra_seg_beg = extra_seg_beg
	rprox.extra_seg_end = extra_seg_end
	rprox
end

#Compute the proximity point between rail and plate contour 
def along_proximity_at_contours(rprox)
	#Initialization
	pts_rail = rprox.pts_rail
	pts_plate = rprox.pts_plate
	
	#Computing the proximity
	res = G6.proximity_curves pts_rail, pts_plate
	rseg = res[1]
	rpt = res[3]
	rdist = pts_rail[rseg-1].distance rpt
	
	#Possible intersection at extremity when applicable
	puts "along Prox"
	if !rprox.loop_rail
		puts "loop rail"
		nr = pts_rail.length - 1
		extra_seg_beg = rprox.extra_seg_beg
		extra_seg_end = rprox.extra_seg_end
		if rseg == 1 && rdist == 0
			res = G6.proximity_curves extra_seg_beg, pts_plate
			rpt = res[3]
			rseg = 0
			rdist = extra_seg_beg[0].distance(rpt)
		elsif rseg == nr && rpt == extra_seg_end[0]
			res = G6.proximity_curves extra_seg_end, pts_plate
			rpt = res[3]
			rseg = nr + 1
			rdist = extra_seg_end[0].distance(rpt)
		end					
	end		

	#Evaluate if there is a contact point
	dcontact = rpt.distance res[4]
	if dcontact < 0.0001
		puts "YA contact"
		contact = 1
	else	
		dbary = res[4].distance rprox.plate.bary
		contact = (dcontact < 0.05 * dbary) ? -1 : 0
	end
	
	#Storing Proximity Information
	rprox.rseg = rseg
	rprox.rpt = rpt
	rprox.rdist = rdist
	rprox.pseg = res[2]
	rprox.ppt = res[4]
	rprox.dcontact = dcontact
	rprox.contact = contact
end
 
#Compute the proximity point between rail and plate contour 
def along_proximity_by_plane(rprox, ptcontact)
	#Initialization
	plate = rprox.plate
	plane = plate.plane
	pts_rail = rprox.pts_rail
	nr = pts_rail.length-1

	#Computing the intersection of the plane and the rail
	lsinter = []				
	for iseg in 1..nr
		ptbeg = pts_rail[iseg-1]
		ptinter = G6.intersect_segment_plane ptbeg, pts_rail[iseg], plane
		lsinter.push [iseg, ptinter, ptinter.distance(ptbeg), ptinter.distance(ptcontact)] if ptinter
	end	
	
	#Trying extremities unless rail is loop
	unless rprox.loop_rail
		extra_seg_beg = rprox.extra_seg_beg
		ptbeg = pts_rail[0]
		ptinter = G6.intersect_segment_plane extra_seg_beg[0], extra_seg_beg[1], plane
		lsinter.push [0, ptinter, ptinter.distance(ptbeg), ptinter.distance(ptcontact)] if ptinter
		
		ptbeg = pts_rail[-1]
		extra_seg_end = rprox.extra_seg_end
		ptinter = G6.intersect_segment_plane extra_seg_end[0], extra_seg_end[1], plane
		lsinter.push [nr+1, ptinter, ptinter.distance(ptbeg), ptinter.distance(ptcontact)] if ptinter	
	end
	
	return nil if lsinter.empty?
	lsinter.sort! { |a, b| a[3] <=> b[3] }
	iseg, ptinter, rdist = lsinter[0]
	
	#Updating the Proximity information
	rprox.rseg = iseg
	rprox.rpt = ptinter
	rprox.rdist = rdist
end
	
#Compute the proximity point between rail and plate contour 
def along_proximity_at_barycenter(rprox)
	#Initialization
	bary = rprox.plate.bary
	pts_rail = rprox.pts_rail
	
	#Computing the proximity of barycenter and rail
	res = G6.proximity_point_curve bary, pts_rail
	rseg = res[1]
	rpt = res[3]
	rdist = pts_rail[rseg-1].distance rpt
	
	#Possible intersection at extremity when applicable
	if !rprox.loop_rail
		nr = pts_rail.length - 1
		extra_seg_beg = rprox.extra_seg_beg
		extra_seg_end = rprox.extra_seg_end
		if rseg == 1 && rdist == 0
			res = G6.proximity_point_curve bary, extra_seg_beg
			rpt = res[3]
			rseg = 0
			rdist = extra_seg_beg[0].distance(rpt)
		elsif rseg == nr && rpt == extra_seg_end[0]
			res = G6.proximity_point_curve bary, extra_seg_end
			rpt = res[3]
			rseg = nr + 1
			rdist = extra_seg_end[0].distance(rpt)
		end					
	end		

	#Storing Proximity Information
	rprox.rseg = rseg
	rprox.rpt = rpt
	rprox.rdist = rdist
	rprox.pseg = nil
	rprox.ppt = nil
	rprox.dcontact = rpt.distance(bary)
	rprox.contact = 0
end

#Adjust the proximity points to possibly fit the vertices of rail and contours 
def along_adjust_proximity(lcrossings, original_rail)
	prox_factor = 0.2
	
	#Adjusting proximity along rail, and along plate if applicable
	lcrossings.each do |cross|
		#plate, rseg, d, rpt, pseg, ppt, dcontact, contact, iplate, pts_plate = cross
		pts_plate = cross.pts_plate
		rseg = cross.rseg
		rpt = cross.rpt
		ppt = cross.ppt
		
		#Along rail
		rpt2 = along_adjust_point original_rail, rseg, rpt, prox_factor
		if rpt2
			cross.rdist = 0
			cross.rpt = rpt2
			cross.dcontact = rpt2.distance(ppt) if ppt
		end
		
		#Along plate
		next unless ppt
		pseg = cross.pseg
		ppt2 = along_adjust_point pts_plate, pseg, ppt, prox_factor
		cross.ppt = ppt2 if ppt2
	end
end

#Adjust a point rpt to the extremity of a segment [pt1, pt2]
def along_adjust_point(rail, rseg, rpt, factor)
	nrail = rail.length-1
	if rseg == 0
		i1 = 0
	elsif rseg > nrail
		i1 = -2
	else
		i1 = rseg-1
	end
	pt1 = rail[i1]
	pt2 = rail[i1+1]
	d = factor * pt1.distance(pt2)
	if rpt.distance(pt1) < d
		rpt = pt1
	elsif rpt.distance(pt2) < d
		rpt = pt2
	else 
		rpt = nil
	end
	rpt
end

#------------------------------------------------------------------------ 
# Single rail and plate
#------------------------------------------------------------------------ 

#Generate the 3 types of links dor each method: stretch, offset and sweep
def along_generate_all_single_links(lcrossings, original_rail)
	@hsh_along_links = {}
	
	[:offset, :stretch, :sweep].each do |method|
		@lst_links = []
		along_generate_single_links method, lcrossings, original_rail
		@hsh_along_links[method] = @lst_links
		@lst_links.each do |link|
			link.hiborders = link.plates.collect { |plate| plate.pts + ((plate.loop) ? [plate.pts[0]] : []) }
		end
	end
end

#Generate the plates and crossing information when there is a single plate 
def along_generate_single_links(method, lcrossings, original_rail)
	#Initialization
	loop_rail = (original_rail.first == original_rail.last)
	cross0 = lcrossings[0]
	plate1 = cross0.plate
	iseg = cross0.rseg
	ptinter = cross0.rpt
	
	#Recalibrating the rail if it is a loop, so that ptinter is the first point
	if loop_rail && original_rail.first != ptinter
		new_rail = (original_rail[iseg] == ptinter) ? [] : [ptinter]
		new_rail += original_rail[iseg..-1]
		new_rail += original_rail[1..iseg-1] if iseg > 1
		new_rail.push ptinter unless new_rail.last == ptinter
		original_rail = new_rail
		iseg = 1
	end
	
	#Computing the generated shape	
	if method == :offset
		newpt_beg = []
		newpt_end = []
		direction = along_direction_offset original_rail, ptinter
		offset_info = G6.offset3d_info original_rail
		crvpts1 = plate1.pts
		crvpts1 = crvpts1 + [crvpts1.first] if plate1.loop
		crvpts1.each do |pt|
			offpts = G6.offset3d_at_point offset_info, pt, iseg, direction, true
			newpt_beg.push offpts[0]
			newpt_end.push offpts[-1]
		end	
		
	elsif method == :sweep
		newpt_beg = []
		newpt_end = []
		crvpts1 = plate1.pts
		crvpts1 = crvpts1 + [crvpts1.first] if plate1.loop
		d, rseg, cseg, ptr, ptc = G6.proximity_curves original_rail, crvpts1
		
		ptbeg = original_rail[0]
		ptend = original_rail[-1]
		if ptr != ptc
			vec = ptr.vector_to ptc
			ptbeg = ptbeg.offset vec
			ptend = ptend.offset vec
		end
		
		offset_info = G6.offset3d_info crvpts1
		direction = along_direction_offset crvpts1, ptc
		newpt_beg = G6.offset3d_at_point offset_info, ptbeg, cseg, direction, true
		newpt_end = G6.offset3d_at_point offset_info, ptend, cseg, direction, true
		
	else
		vec = ptinter.vector_to original_rail[0]
		if vec.valid?
			tr = Geom::Transformation.translation vec
			newpt_beg = plate1.pts.collect { |pt| tr * pt }
		else
			newpt_beg = plate1.pts
		end	
		vec = ptinter.vector_to original_rail[-1]
		if vec.valid?
			tr = Geom::Transformation.translation vec
			newpt_end = plate1.pts.collect { |pt| tr * pt }
		else
			newpt_end = plate1.pts
		end	
	end
	
	#Plate is a loop	
	if plate1.loop
		newpt_beg.push newpt_beg[0]
		newpt_end.push newpt_end[0]
	end

	#Generating the plates
	plate_beg = plate_create newpt_beg
	plate_end = plate_create newpt_end

	#Normal directions at rail
	ptbeg = original_rail[0]
	ptend = original_rail[-1]
	plate_beg.vecrail = ptbeg.vector_to original_rail[1]
	plate_end.vecrail = original_rail[-2].vector_to ptend
	
	#Creating the Rail information structure
	nr = original_rail.length - 1
	rail_param = F6_Curviloft::CVL__LOFT_RailParam.new
	rail_param.full_rail = rail_param.part_rail = original_rail
	rail_param.full_rail_len = rail_param.part_rail_len = G6.curl_length(original_rail)
	rail_param.seg_info1 = [0, ptbeg]
	rail_param.seg_info2 = [nr, ptend]
	
	#Creating the link
	link = link_create plate_beg, plate_end	

	link.rail_param = rail_param
	link.angle_offset = 0
	link.duplicated = true
	link.no_edition = loop_rail
	
	@lst_links
end

def along_direction_offset(rail, pt)
	if pt == rail.first
		direction = +1
	elsif pt == rail.last
		direction = -1
	else
		direction = 0
	end	
	direction
end

#Determine the best rail if there are several 
def along_best_rail(all_crossings)
	puts "best rail #{all_crossings.length}"
	return all_crossings[0] if all_crossings.length == 1
	
	
	#Sorting the crossings by contact information
	lsx = []
	all_crossings.each_with_index do |crossings, i|
		dcontact = 0
		ncontact = mcontact = 0
		crossings[2].each do |cross|
			dcontact += cross.dcontact
			ncontact += 1 if cross.contact > 0
			mcontact += 1 if cross.contact < 0
		end	
		lsx.push [-ncontact, -mcontact, dcontact, crossings]
	end
	
	lsx.sort! { |a, b| (a[0] == b[0]) ? ((a[1] == b[1]) ?  a[2] <=> b[2] : a[1] <=> b[1]) : a[0] <=> b[0] }
	lsx[0][3]
end

#Extend the rail at extremities if not a loop
def along_extra_segment_beg(pts_rail)
	pt1 = pts_rail[0]
	ptn = pts_rail[1]
	vec = ptn.vector_to pt1
	d = pt1.distance(ptn)
	ptbeg = pt1.offset vec, 100 * d
	[pt1, ptbeg]
end

def along_extra_segment_end(pts_rail)
	pt1 = pts_rail[-1]
	ptn = pts_rail[-2]
	vec = ptn.vector_to pt1
	d = pt1.distance(ptn)
	ptend = pt1.offset vec, 100 * d
	[pt1, ptend]
end
 
#Compute the rail portion for the link 
def along_adjust_full_rail(rail, iseg1, iseg2)
	return [rail, iseg1, iseg2] if iseg1 <= iseg2
	
	ncrv = rail.length - 1
	iseg_mid = (iseg1 + iseg2) / 2
	new_iseg1 = iseg1 - iseg_mid
	new_iseg2 = ncrv - iseg_mid + iseg2
	newrail = rail[iseg_mid..-1] + rail[1..iseg_mid]
	[newrail, new_iseg1, new_iseg2]
end
 
#Compute the rail portion for the link 
def along_rail_portion(full_rail, pt1, pt2, iseg1, iseg2)
	rail = (pt1 == full_rail[iseg1]) ? [] : [pt1]
	for i in iseg1..iseg2-1
		rail.push full_rail[i]
	end
	rail.push pt2 unless rail.last == pt2
	rail
end
   
#----------------------------------------------------------------------------
# ALONG: construction of the shape
#----------------------------------------------------------------------------

#Check if links must be switched in case of a single plate
def along_switch_links
	return false unless @hsh_along_links
	method = @hprop[:along_method]
	links = @hsh_along_links[method]
	return false if links == @lst_links
	@lst_links = links
	@active_link = links[0] if @active_link
	true
end

#Transform the a given shape along 
#Construct the Along_path curves for a link
def along_junction_construct(link)	
	method = @hprop[:along_method]
	
	#Constructing the junctions by Offset 3D
	if method == :offset
		lbz_pts = along_offset_construct link

	#Constructing the junction by Sweeping
	elsif method == :sweep
		lbz_pts = along_sweep_construct link
	
	#Constructing the junction by Stretching
	else
		lbz_pts = along_stretch_construct link
	end
	
	lbz_pts
end

#Interpolate the rail (with equal number of points)
#return the rail interpolated and the list of index of points added
def along_oversample_rail(link, method)
	rail_param = link.rail_param
	iseg1, ptbeg = rail_param.seg_info1
	iseg2, ptend = rail_param.seg_info2
	
	if method == :offset
		rail = rail_param.full_rail
		dtot = rail_param.full_rail_len
	else
		rail = rail_param.part_rail
		dtot = rail_param.full_rail_len
	end
	
	#No Oversampling
	return [rail, iseg1, iseg2] unless hprop_get(:option_sample, link)
	fsample = hprop_get(:factor_sample, link)
	return [rail, iseg1, iseg2] if fsample < 1

	#Initialization
	ratio = (dtot > 0) ? 1.0 / dtot : 1.0
	factor = fsample
	
	#Going through each segment of the rails
	newrail = [rail[0]]
	len = rail.length-1
	for i in 1..len
		pt_prev = rail[i-1]
		pt = rail[i]
		d = pt.distance(pt_prev)
		
		n = (d * ratio * factor).round	
		step = 1.0 / (n + 1)
		for j in 1..n
			f = j * step
			newrail.push Geom.linear_combination(1-f, pt_prev, f, pt)
		end		
		newrail.push pt
	end
	
	#Reconstructing the iseg index
	if method == :offset
		iseg1 = along_recalculate_iseg(rail, newrail, iseg1, ptbeg)
		iseg2 = along_recalculate_iseg(rail, newrail, iseg2, ptend)
	end
	
	#Returning the new rail
	[newrail, iseg1, iseg2]
end

#Recalculate the segment index for the sampled rail
def along_recalculate_iseg(rail, newrail, iseg, ptinter)
	nrail = rail.length - 1
	return iseg if iseg == 0 || iseg > nrail
	
	ptseg = rail[iseg-1]
	dseg = ptseg.distance ptinter
	d = nil
	for i in 0..newrail.length-2
		pt = newrail[i]
		return i if pt == ptinter && i > 0
		d = 0 if pt == ptseg
		if d
			d += pt.distance newrail[i+1]
			return i+1 if d >= dseg
		end	
	end
	iseg
end

#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------
# Curve Stretching
#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------

#Construct the shape with Offset method 
def along_stretch_construct(link)
	lbz_pts = []
	rail, = along_oversample_rail link, :stretch
	loop = (rail.first == rail.last)
	link.pairs.each do |pair|
		lbz_pts.push G6.curl_move_extremities(rail, pair[0].pt, pair[1].pt)
	end
	lbz_pts
end

#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------
# Curve Offsetting
#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------

#Construct the shape with Offset method 
def along_offset_construct(link)
	lbz_pts = []
	rail, iseg1, iseg2 = along_oversample_rail link, :offset
	off = G6.offset3d_info rail
	link.pairs.each do |pair|
		lbz_pts.push G6.offset3d_at_two_points(off, pair[0].pt, pair[1].pt, iseg1, iseg2, true)
	end
	lbz_pts
end

#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------
# Curve Sweeping
#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------

#Construct the shape with Sweep method 
def along_sweep_construct(link)
	rail, = along_oversample_rail link, :sweep
	
	simplify = hprop_get(:option_simplify, link)
	
	#Building the contours
	crvpts1 = link.pairs.collect { |pair| pair[0].pt }
	crvpts2 = link.pairs.collect { |pair| pair[1].pt }
		
	crvpts1 = crvpts1 + [crvpts1[0]] if link.plates[0].loop
	crvpts2 = crvpts2 + [crvpts2[0]] if link.plates[1].loop
	
	#Calculating the profil for rail with first curve	
	sweep_along_rail rail, crvpts1, crvpts2, simplify
end

#Compute the list of curves making a sweep of rail between the two curves crvpts1 and crvpts2
def sweep_along_rail(rail, crvpts1, crvpts2, simplify=false)
	#crvpts1 = G6.curl_deduplicate crvpts1
	#crvpts2 = G6.curl_deduplicate crvpts2
	nc = crvpts1.length - 1
	lbz_pts = []
	
	#Case of a circular Rail, which also means that crvpts1 and crvpts2 are identical
	if rail[0] == rail[-1]
		d, rseg, cseg, ptr, ptc = G6.proximity_curves rail, crvpts1
		direction = along_direction_offset crvpts1, ptr
		offset_info = G6.offset3d_info crvpts1	
		llpt1 = rail.collect { |pt| G6.offset3d_at_point offset_info, pt, cseg, direction, simplify }
		for i in 0..nc
			ptbeg = crvpts1[i]			
			pts1 = llpt1.collect { |ls| ls[i] }
			offset_info = G6.offset3d_info pts1
			pts1 = G6.offset3d_at_point offset_info, ptbeg, rseg, 0, simplify
			lbz_pts.push pts1
		end	
		return lbz_pts
	end
	
	#Computing the offset of the curves
	d, rseg, cseg, ptr, ptc = G6.proximity_curves rail, crvpts1
	direction = along_direction_offset crvpts1, ptr
	offset_info = G6.offset3d_info crvpts1	
	llpt1 = rail.collect { |pt| G6.offset3d_at_point offset_info, pt, cseg, direction, simplify }
	
	d, rseg, cseg, ptr, ptc = G6.proximity_curves rail, crvpts2
	direction = along_direction_offset crvpts2, ptr
	offset_info = G6.offset3d_info crvpts2	
	llpt2 = rail.collect { |pt| G6.offset3d_at_point offset_info, pt, cseg, direction, simplify }
		
	for i in 0..nc
		ptbeg = crvpts1[i]
		ptend = crvpts2[i]
		
		pts1 = llpt1.collect { |ls| ls[i] }
		pts1 = G6.curl_move_extremities pts1, ptbeg, ptend
		
		pts2 = llpt2.collect { |ls| ls[i] }
		pts2 = G6.curl_move_extremities pts2.reverse, ptend, ptbeg
		
		bz = G6.curl_average_curve pts1, pts2.reverse, true
		lbz_pts.push bz
	end	
	
	lbz_pts
end

end	#End Class CVL_LoftAlgo

end	#End Module F6_Curviloft
