=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Designed by Fredo6 - Copyright November 2008

# 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			:   FredoScale_Tool.rb
# Original Date	:   8 Mar 2009 - version 2.0
# Description	:   Implement the speific methods of the interactive Tools of FredoScale
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_FredoScale

#--------------------------------------------------------------------------------------------------------------
# Scale in box class: specific methods
#--------------------------------------------------------------------------------------------------------------			 				   

class ScaleTool

def tool_behavior_default
	@protractor = nil
	@mode_vcbangle = false
	@authorize_livedeform = false	
	@mode_pads = false
	@skip_handles = ['CMF', 'C', 'C']
	@mode_postvalidate = false
	@mode_divider = false
	@mode_dice = false
	@mode_new_edges = false
	@mode_spy = false
	@mode_delta = false
end

#Behavior of the Scale tool
def tool__behavior
	tool_behavior_default
	@protractor = nil
	@mode_vcbangle = false
	@authorize_livedeform = true	
	@mode_pads = false
	@skip_handles = ['CMF', 'C', 'C']
	@mode_postvalidate = false
	@mode_divider = false
	@mode_dice = false
	@mode_new_edges = false
	@mode_spy = false
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxScale]
	@color_box_target = MYDEFPARAM[:DEFAULT_Color_BoxScaleTarget]
end

#Compute the Scaling transformation
def tool__compute_transformation
	ts = Geom::Transformation.scaling @scales[0], @scales[1], @scales[2]
	@tr_new = @tr_box * ts * @tr_box_inv
	@tr_boxpt = @tr_new * @tr_previous
	
	@tr_deform = @tr_new
end

#Proceed with the Tapering deformation of the box
def tool__scale_the_box(view)
	@bbox = @bbox.collect { |pt| @tr_boxpt * pt }
end

#deform the entities for Scale
def tool__deform_entities
	@model.active_entities.transform_entities @tr_deform, @selection
end

#Compute the new box after validation in Scale mode
def tool__compute_box_after
	@bbox
end

#Collect the list of edge vertices with absolute coordinates (common to all tools except Stretch)
def tool__compute_wireframe
	return [] unless @tr_new
	@lst_wireframe.collect { |pt| @tr_deform * pt }
end

def tool__cancel
	@tr_deform = @tr_identity
end

#Start a new session (Default for all tools)
def tool__new_session(new_orientation=false)

end

end	# class ScaleTool

#--------------------------------------------------------------------------------------------------------------
# Taper in box class
#--------------------------------------------------------------------------------------------------------------			 				   

class TaperTool < ScaleTool

#Behavior of the Taper tool
def tool__behavior
	return super if @disguised
	tool_behavior_default

	@mode_taper = true
	@mode_vcbangle = false
	@authorize_livedeform = false
	@mode_pads = true
	@skip_handles = ['CMF', 'CMF', 'CF']
	@mode_postvalidate = true
	@mode_spy = true
	@mode_dice = false
	@mode_new_edges = true
	@dim_disguise = 1
	@type_disguise = 'Scale'
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxTaper]
	@color_box_target = MYDEFPARAM[:DEFAULT_Color_BoxTaperTarget]	
end

#Compute the Tapering transformation
def tool__compute_transformation
	return super if @disguised

	#Scaling Transformation for the face
	ts = Geom::Transformation.scaling @scales[0], @scales[1], @scales[2]
	@tr_new = @tr_box * ts * @tr_box_inv
	@tr_boxpt = @tr_new * @tr_previous
	
	#Caculating the origin
	ih = @highlight.ih
	oih = @highlight.oih
	iface = @face_full[@highlight.iface]

	pt1 = @handles[iface[0]].pt3d
	pt2 = @handles[iface[1]].pt3d
	pt3 = @handles[iface[2]].pt3d
	vec1 = pt1.vector_to pt2
	vec2 = pt2.vector_to pt3
	pt_opp = @handles[@handles[ih].opp].pt3d
	if @dim == 2 
		vec = @handles[ih].pt3d.vector_to @handles[oih].pt3d
		plane = [pt_opp, (vec1 * vec2) * vec]
	else
		plane = [pt_opp, vec1 * vec2]
	end	
	ipivot = @from_center ? @highlight.ihcenter : @highlight.oih
	pivot =  @handles[ipivot].pt3d
	origin = pivot.project_to_plane plane
	axez = origin.vector_to pivot
	height = origin.distance pivot
	axex = @haxes[0]
	xscale = @scales[@iaxes[0]]
	yscale = (@iaxes[1]) ? @scales[@iaxes[1]]: 1.0

	@tr_deform = G6::TR_Tapering.new(origin, [axex, axez * axex, axez], xscale, yscale, height)
end

#Proceed with the Tapering deformation of the box
def tool__scale_the_box(view)
	return super if @disguised

	#Creating the new box
	if @dim == 3
		n = 7
		lface = @face_full[@highlight.iface][0..3]
	else
		n = 3
		lface = [@highlight.ih, @highlight.oih]
	end
	for i in 0..n
		 @bbox[i] = @tr_boxpt * @bbox[i] if lface.include?(i)
	end			
end
	
#deform the entities for Taper
def tool__deform_entities
	return super if @disguised

	@tr_deform.transform_entities @selection, @new_edgeprop
end

#Compute the new box after validation in Taper mode
def tool__compute_box_after	
	return super if @disguised

	ls = @scales.collect { |s| (s < 1.0) ? 1.0 : s }
	ts = Geom::Transformation.scaling ls[0], ls[1], ls[2]
	tnew = @tr_box * ts * @tr_box_inv
	newbox = @bbox0.collect { |pt| tnew * pt }
	newbox
end
	
end	# class TaperTool

#--------------------------------------------------------------------------------------------------------------
# Planar Shear in box class
#--------------------------------------------------------------------------------------------------------------			 				   

class PlanarShearTool < ScaleTool

#Behavior of the Planar Shear tool
def tool__behavior
	return super if @disguised
	tool_behavior_default

	@mode_planarshear = true
	@mode_vcbangle = true
	@authorize_livedeform = true
	@mode_pads = true
	@skip_handles = ['CMF', 'CMF', 'CFB']
	@mode_postvalidate = true
	@mode_spy = false
	@mode_dice = false
	@mode_new_edges = false
	@mode_noask_dimensions = true
	@dim_disguise = 1
	@type_disguise = 'Scale'
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxPlanarShear]
	@color_box_target = MYDEFPARAM[:DEFAULT_Color_BoxPlanarShearTarget]
end

#Compute the PlanarShearing transformation
def tool__compute_transformation
	return super if @disguised
	
	#Calculating the origin, direction vector and perpendicualr vector
	ih = @highlight.ih
	oih = @highlight.oih
	iopp = @handles[ih].opp
	iboxcenter = @handles.length - 1
	iorig = (@from_center) ? iboxcenter : @handles[ih].opp
	origin = @handles0[iorig].pt3d
	vecdir = @handles0[oih].pt3d.vector_to @handles0[ih].pt3d
	normal = @handles0[iopp].pt3d.vector_to @handles0[oih].pt3d

	#Calculating the angle
	tgt = planarshear_angle_from_scale @scales[@iaxes[0]]
	txangle = sprintf("%3.1f", Math.atan2(tgt, 1).radians) + " deg."
	txslope = sprintf("%3.1f", tgt * 100) + "%"
	@text_angle = txangle + ', ' + txslope
	
	#Calculating the transformation
	tr_axe = Geom::Transformation.axes origin, vecdir, normal, vecdir * normal
	tr_axeinv = tr_axe.inverse
	coef = [1, 0, 0, 0, tgt, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
	
	@tr_new = tr_axe * Geom::Transformation.new(coef) * tr_axeinv
	@tr_deform = @tr_new
	@tr_boxpt = @tr_new * @tr_previous
end

#Proceed with the PlanarShearing deformation of the box
def tool__scale_the_box(view)
	return super if @disguised

	#Creating the new box
	if @dim == 3
		n = 7
		lface = @face_full[@highlight.iface][0..3]
	else
		n = 3
		lface = [@highlight.ih, @highlight.oih]
	end
	for i in 0..n
		 @bbox[i] = @tr_boxpt * @bbox[i] if lface.include?(i)
	end			
end

#deform the entities for Planar Shear
def tool__deform_entities
	return super if @disguised

	@model.active_entities.transform_entities @tr_deform, @selection
end

end	# class PlanarShearTool

#--------------------------------------------------------------------------------------------------------------
# Rotate in box class
#--------------------------------------------------------------------------------------------------------------			 				   

class RotateTool < ScaleTool

#Behavior of the Rotate tool
def tool__behavior
	tool_behavior_default
	
	@protractor = G6::ShapeProtractor.new
	@mode_vcbangle = true
	@mode_nocenter = true
	@mode_noask_dimensions = true
	@authorize_livedeform = true	
	@mode_pads = false
	@mode_dice = false
	@mode_new_edges = false
	@skip_handles = ['MF', '', 'CB']
	@mode_postvalidate = false
	@mode_spy = false
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxRotate]
end

#Compute the Scaling transformation
def tool__compute_transformation
	p = @rotate_param
	@tr_new = Geom::Transformation.rotation p.origin, p.normal, p.angle
	@tr_boxpt = @tr_new * @tr_previous	
	@tr_deform = @tr_new
	@text_angle = sprintf("%3.1f", p.angle.radians) + " deg."	
end

#Proceed with the Rotate deformation of the box
def tool__scale_the_box(view)
	@bbox = @bbox.collect { |pt| @tr_boxpt * pt }
	@box_axes = @box_axes.collect { |v| @tr_boxpt * v }
	refresh_handles @view
end

#deform the entities for Scale
def tool__deform_entities
	@model.active_entities.transform_entities @tr_deform, @selection
end

#Compute the new box after validation in Scale mode
def tool__compute_box_after
	@bbox
end

end	# class RotateTool

#--------------------------------------------------------------------------------------------------------------
# Twist in box class: specific methods
#--------------------------------------------------------------------------------------------------------------			 				   

class TwistTool < RotateTool

#Behavior of the Rotate tool
def tool__behavior
	return super if @disguised
	tool_behavior_default

	@protractor = G6::ShapeProtractor.new
	@mode_vcbangle = true
	@mode_nocenter = false
	@mode_noask_dimensions = true
	@dim_disguise = 2
	@type_disguise = 'Rotate'
	@authorize_livedeform = false	
	@mode_dice = true
	@mode_new_edges = true
	@dice_param_def = G6::TR_Dicer.create_param 12
	@mode_pads = false
	@skip_handles = ['C', 'C', 'CBM']
	@mode_postvalidate = true
	@mode_spy = true
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxTwist]
end

#Compute the Twisting transformation
def tool__compute_transformation
	return super if @disguised

	p = @rotate_param
	hcur = @sel_handles0[0]
	hpivot = (@from_center) ? @hcenter : @sel_handles0[1]
	origin = hpivot.pt3d
	height = origin.distance hcur.pt3d
	axes = [p.basedir, p.normal * p.basedir, p.normal]
	@tr_new = G6::TR_Twisting.new origin, axes, p.angle, height, @from_center, @dice_param
	@tr_deform = @tr_new
	@tr_boxpt = @tr_deform
	@text_angle = sprintf("%3.1f", p.angle.radians) + " deg."	
end

#Proceed with the Rotate deformation of the box
def tool__scale_the_box(view)
	return super if @disguised

	@bbox = @bbox0.collect { |pt| @tr_boxpt * pt }
end

#deform the entities for Scale
def tool__deform_entities
	return super if @disguised

	@tr_deform.transform_entities @selection, @new_edgeprop
end

#Compute the new box after validation in Scale mode
def tool__compute_box_after
	return super if @disguised

	@bbox
end

end	# class TwistTool

#--------------------------------------------------------------------------------------------------------------
# Stretch in box class: specific methods
#--------------------------------------------------------------------------------------------------------------			 				   

class StretchTool < ScaleTool

#Behavior of the Mid Stretch tool
def tool__behavior
	return super if @disguised
	tool_behavior_default

	@protractor = nil
	@mode_delta = true
	@mode_vcbangle = false
	@authorize_livedeform = true	
	@mode_pads = false
	@skip_handles = ['CMF', 'CB', 'CMB']
	@mode_postvalidate = false
	@mode_dice = false
	@mode_new_edges = false
	@dim_disguise = 0
	@mode_spy = false
	@mode_divider = true
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxStretch]
	@color_box_target = MYDEFPARAM[:DEFAULT_Color_BoxStretchTarget]
end

#Called when there is  a new session
def tool__new_session(new_orientation=false)
	return unless new_orientation
	ldim = current_box_dimensions
	ptcenter = Geom.linear_combination 0.5, @handles[@num_handles[0]].pt3d, 0.5, @handles[@num_handles[1]].pt3d
	fac = []
	for iaxe in 0..2
		dim = current_box_dimensions[iaxe]
		unless @pos_divider[iaxe]
			fac[iaxe] = 0
			next
		end
		vec = ptcenter.vector_to @pos_divider[iaxe]
		fac[iaxe] = vec.length / dim * 2
		fac[iaxe] = -fac[iaxe] if vec % @box_axes[iaxe] < 0
	end	
	@stretcher = TR_MidStretching.new @selection, @handles.last.pt3d, @box_axes, ldim, fac, @from_center
	@from_center_cur = @from_center
	@need_unique = [true, true, true]
end

#Compute the Stretching transformation
def tool__compute_transformation
	return super if @disguised

	#Deformation of the box
	ts = Geom::Transformation.scaling @scales[0], @scales[1], @scales[2]
	@tr_new = @tr_box * ts * @tr_box_inv
	@tr_boxpt = @tr_new * @tr_previous
	
	#Calculating the parameters of the Strech deformation
	@stretch_iaxe = iaxe = @iaxes[0]
	box_axe = @box_axes[iaxe]
	pt = @sel_handles0[0].pt3d
	pt2 = @tr_new * pt
	vecorient = @hcenter.pt3d.vector_to pt
	@deltavec = pt.vector_to pt2 unless @deltavec
	reverse = (box_axe % vecorient < 0)
	@stretch_sense = (@from_center) ? ((reverse) ? [-1, 1] : [1, -1]) : ((reverse) ? [0, 1] : [1, 0])
end

#Proceed with the Stretch deformation of the box
def tool__scale_the_box(view)
	return super if @disguised

	@bbox = @bbox.collect { |pt| @tr_boxpt * pt }
end

#deform the entities for Stretch
def tool__deform_entities
	return super if @disguised

	#if @stretcher.need_make_unique?(@stretch_iaxe)
	if @need_unique[@stretch_iaxe] && @stretcher.need_make_unique?(@stretch_iaxe)
		commit_operation if @operation
		start_operation @title_tool + " Unique axe #{@stretch_iaxe}"
		@stretcher.proceed_make_unique @stretch_iaxe
		commit_operation 1
		@need_unique[@stretch_iaxe] = false
		start_operation
	end
	
	@stretcher.deform @stretch_iaxe, @stretch_sense, @deltavec if @deltavec.valid?
end

#Compute the new box after validation in Scale mode
def tool__compute_box_after
	return super if @disguised

	@bbox
end

#Collect the list of edge vertices with absolute coordinates (common to all tools except Stretch)
def tool__compute_wireframe
	return super if @disguised

	return [] unless @tr_new && @deltavec.valid?
	@stretcher.get_wireframe @stretch_iaxe, @stretch_sense, @deltavec
end

end	# class StretchTool

#--------------------------------------------------------------------------------------------------------------
# Radial Bending in box class
#--------------------------------------------------------------------------------------------------------------			 				   

class RadialBendTool < ScaleTool

#Behavior of the Radial bending tool
def tool__behavior
	tool_behavior_default
	
	@protractor = G6::ShapeProtractor.new
	@mode_vcbangle = true
	@mode_nocenter = true
	@mode_noask_dimensions = true
	@authorize_livedeform = false	
	@mode_pads = false
	@mode_new_edges = false
	@skip_handles = ['MF', '', 'CB']
	@mode_postvalidate = true
	@mode_spy = false
	@color_box = MYDEFPARAM[:DEFAULT_Color_BoxRadialBend]
end

#Compute the Radial Bending transformation
def tool__compute_transformation
	p = @rotate_param
	hcur = @sel_handles[0]
	hpivot = (@from_center) ? @hcenter : @sel_handles0[1]
	origin = hpivot.pt3d
	height = origin.distance hcur.pt3d
	axes = [p.basedir, p.normal * p.basedir, p.normal]
	@tr_new = G6::TR_RadialBending.new p.origin, axes, p.angle, height

	p = @rotate_param
	@tr_new = Geom::Transformation.rotation p.origin, p.normal, p.angle
	@tr_boxpt = @tr_new * @tr_previous	
	@tr_deform = @tr_new
	@text_angle = sprintf("%3.1f", p.angle.radians) + " deg."	
end

#Proceed with the Radial Bending deformation of the box
def tool__scale_the_box(view)
	@bbox = @bbox.collect { |pt| @tr_boxpt * pt }
	@box_axes = @box_axes.collect { |v| @tr_boxpt * v }
	refresh_handles @view
end

#deform the entities for Radial bending
def tool__deform_entities
	@model.active_entities.transform_entities @tr_deform, @selection
end

#Compute the new box after validation in Rdial Bending mode
def tool__compute_box_after
	@bbox
end

end	# class RadialBendTool

end	#End Module F6_FredoScale
