=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# © Copyright May 2014
# Designed and developped May 2014 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			:   VisuHoleStencilTool.rb
# Original Date	:   18 May 2014
# Description	:   VisuHole Interactive Tool
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_VisuHole

T6[:TIP_SelectTopParent] = "Recurse from Top Parent group or component"
T6[:TIP_SelectRecurse] = "Recurse from Picked group and component"
T6[:BOX_ApplyChange] = "APPLY|CHANGE"
T6[:TIP_ApplyChange] = "Recompute the intersections after a change of parameters"
T6[:TIP_StampPunch] = "Punch the hole"
T6[:TIP_StampMark] = "Stamp with Guide lines instead of edges"
T6[:TIP_ThruTitle] = "Drill a hole thru the selection"
T6[:TIP_ThruSingleTube] = "Drill limited to First Tube only"
T6[:TIP_ThruNoFace] = "Do not create the tubes, just create intersections"
T6[:TIP_ThruMark] = "Create the hole with Guide lines instead of edges"
T6[:TIP_StampTitle] = "Stamp the stencil shape"
T6[:BOX_StampCarve] = "Carve"
T6[:TIP_StampCarve] = "Extrusion by Carving the selection"
T6[:BOX_StampEmboss] = "Emboss"
T6[:TIP_StampEmboss] = "Extrusion by Embossing the selection"
T6[:BOX_DrillThru] = "Drill Thru"
T6[:BOX_Stamp] = "Stamp"
T6[:TIP_StampOffset] = "Offset for Carving or Embossing (type value in VCB)"
T6[:PROMPT_StampOffset] = "Offset for Carving or Embossing"
T6[:TIP_StampKeepPlane] = "Direction for carving or Embossing is the Master plane"
T6[:TIP_StampFlat] = "Flatten the extruded faces"
T6[:TIP_Surfloc] = "Follow the surface of selection to place the stencil shape (instead of straight projection)"
T6[:TIP_CustomMaterial] = "Custom material used for painting tubes and extrusions"
T6[:TIP_CutInGroup] = "GROUP IN: Put the intersections or extrusions in a group for each solid"	
T6[:TIP_CutOutGroup] = "GROUP OUT: Put the intersections or extrusions in a master group at top level - leave model untouched"	
T6[:TIP_NoUseMaterial] = "NO Material"
T6[:TIP_NoGroup] = "NO Grouping"
T6[:TIT_OriginMessage] = "Double-Click to execute or Click to pick and orientate"	
T6[:TIT_TargetMessage] = "Move or Drag to orientate"	
T6[:MSG_NoStencil] = "Please pick first a Stencil Shape in the model"
T6[:ERR_Geometry] = "Error in generation of intersections"

#--------------------------------------------------------------------------------------------------------------
# Top Calling functions: create the classes and launch the tools
#--------------------------------------------------------------------------------------------------------------			 				   

#Actual launching of menus	
def F6_VisuHole.action__mapping(action_code, *args)
	case action_code
	when :stencil
		Sketchup.active_model.select_tool VisuHoleTool.new(action_code, *args)
	end	
end

#=============================================================================================
#=============================================================================================
# Class VisuHoleTool: main class for the Interactive tool
#=============================================================================================
#=============================================================================================

class VisuHoleTool < Traductor::PaletteSuperTool

@@last_custom_material = nil unless defined?(@@last_custom_material)

#---------------------------------------------------------------------------------------------
# INIT: Initializations
#---------------------------------------------------------------------------------------------

def initialize(action_code, *args)
	#Basic initialization
	@model = Sketchup.active_model
	@view = @model.active_view
	@rendering_options = Sketchup.active_model.rendering_options
	@tr_id = Geom::Transformation.new
	@duration_long_click = 0.8
	@ogl = Traductor::OpenGL_6.new
	@action_code = action_code
	@title = T6[:VHL_Stencil_Menu]
	
	#Parsing the arguments
	args.each { |arg| arg.each { |key, value|  parse_args(key, value) } if arg.class == Hash }

	#Loading default parameters
	proc = self.method "notify_from_defparam"
	MYDEFPARAM.add_notify_proc(proc, true)

	#Loading parameters
	@option_recurse = true
	@option_from_top_parent = true
	
	#Current material
	@custom_material = @@last_custom_material
	@custom_material = nil if @custom_material && !@custom_material.valid?
	@use_cut_material = false
	
	@dico_name = "Fredo6_VisuHole"
	@dico_attr = "Parameters"
	parameter_load

	#Material manager
	@matos = Traductor::MaterialManager.new
	
	#Material Picker
	hsh = { :notify_proc => self.method("notify_from_material_picker") }
	@matpick = Traductor::MaterialPicker.new hsh
	
	#Static initialization
	init_cursors
	init_colors
	init_messages

	#Initializing the Key manager
	@keyman = Traductor::KeyManager.new() { |event, key, view| key_manage(event, key, view) }

	#Initializing the Zoom Manager
	@zoom_void = G6::ZoomVoid.new
	
	#Create the Origin-Target Picker
	hsh = { :ignore_selection => true, :color_box_distance => @color_box_distance, :hide_distance => true,
			:option_implicit_plane => [:best_normal, :bissector], :notify_proc => self.method("notify_from_otpicker") }
	@otpicker = Traductor::OriginTargetPicker.new @hsh_parameters, hsh

	#Creating the Intersector
	@intersector = Traductor::StencilIntersector.new
	
	#Creating the Stencil Manager
	nh = MYDEFPARAM[:VHL_OPTION_HistoryKeep]
	hsh = { :notify_proc => self.method("notify_from_stencil_manager"), :history_keep => nh }
	@stencil_man = Traductor::StencilManager.new hsh
	
	#Creating the palette manager and texts
	init_palette
	
	#Initialize the VCB
	init_VCB
end

#INIT: Parse the arguments of the initialize method
def parse_args(key, value)
	skey = key.to_s
	case skey
	when /from/i
		@invoked_from = value
	end	
end	

#INIT: Initialize texts and messages
def init_messages
	@mnu_exit = T6[:T_BUTTON_Exit]	
	@mnu_abort = T6[:T_STR_AbortTool]
	@msg_double_click_exit = T6[:T_INFO_DoubleClickExit]
	
	@msg_pick_origin = T6[:T_MSG_OriginPick] + " - " + T6[:TIT_OriginMessage]
	@msg_pick_target = T6[:T_MSG_TargetPick] + " - " + T6[:TIT_TargetMessage]
	@msg_no_stencil = T6[:MSG_NoStencil]
end

#INIT: Initialize colors
def init_colors
	@su_selection_color = @rendering_options['HighlightColor']
	@color_box_distance = ['lightgreen', 'green']
	@color_message = 'khaki'
	@hsh_boxinfo_tip = { :bk_color => 'lightyellow', :fr_color => 'yellow' }	
	@color_stencil = Sketchup::Color.new 'yellow'
	@color_stencil.alpha = 0.4
	
	@color_but_hi = 'lightgreen'
	@color_but_hi_m = 'thistle'
	@color_but_tit = 'lavender'
	@color_but_tit_n = 'lightblue'
	@color_but_hi_s = 'white'
	
	@cut_style_color = cut_style_compute_color
	
end

#INIT: Initialize cursors
def init_cursors
	@id_cursor_default = MYPLUGIN.create_cursor "stencil", 2, 2
	@id_cursor_hourglass_red = Traductor.create_cursor "Cursor_hourGlass_Red", 16, 16		
	@id_cursor = @id_cursor_default
end

#INIT: Initialize instructions for icon when no stencil
def init_instructions_no_stencil
	bk_color = 'lightgrey'
	fr_color = 'red'
	size = 32
	pts = G6.pts_rectangle 0, -0.5, size, size
	@no_stencil_instructions = [[GL_POLYGON, pts, bk_color], [GL_LINE_LOOP, pts, fr_color, 2, '']]
	@no_stencil_instructions += @ogl.draw_proc(:std_pick_shape, size, size)
	ts = Geom::Transformation.scaling ORIGIN, 1, -1, 1
	tt = Geom::Transformation.translation Geom::Vector3d.new(0, size, 0)
	@tr_no_stencil_instructions = tt * ts		
end

#INIT: Notification when default parameters are changed
def notify_from_defparam(key, val)
	apply_change_compute
end

#---------------------------------------------------------------------------------------------
# PARAMETER: Parameter Management and storage
#---------------------------------------------------------------------------------------------

#Persistent parameters and defaults
@@hsh_parameters_persist = {} unless defined?(@@hsh_parameters_persist) && @@hsh_parameters_persist.length > 0

#PARAMETER: Load the parameters from the model attribute and the defaults
def parameter_load
	@hsh_parameters = {}
	
	#Defaults
	@hsh_param_default = {
		:option_recurse => true,
		:option_from_top_parent => true,
		:option_inference_comp => true,
		:option_planar => true,
		:option_planar_code => :no,
		:option_vector => false,
		:cut_style => :thru,
		:carve_offset => 0,
		:thru_no_face => false,
		:thru_nb_tube => nil,
		:thru_mark => false,
		:stamp_mark => false,
		:stamp_punch => false,
		:carve_flat => false,
		:carve_plan => false,
		:cut_in_group => false,
		:cut_out_group => false,
		:use_cut_material => false,
		:surfloc => false
	}
		
	#Calculating the appropriate parameters
	@hsh_parameters = {}
	@hsh_parameters.update @hsh_param_default
	sparam = Traductor::Default.read @dico_name, @dico_attr
	hsh = eval(sparam) rescue {}
	hsh = {} unless hsh
	
	#Settings for the Origin-Traget picker
	hsh[:option_inference_comp] = false
	hsh[:option_inference_remote] = false
	hsh[:option_planar] = true
	hsh[:option_planar_code] = :no
	hsh[:option_vector] = false
	hsh[:option_vector_code] = :no
	
	#Settings for VisuHole
	@@hsh_parameters_persist.update hsh
	@hsh_parameters.update @@hsh_parameters_persist 
	
	@option_recurse = @hsh_parameters[:option_recurse]
	@option_from_top_parent = @hsh_parameters[:option_from_top_parent]
	
	@cut_style = @hsh_parameters[:cut_style]
	@carve_offset = @hsh_parameters[:carve_offset]
	@thru_no_face = @hsh_parameters[:thru_no_face]
	@thru_nb_tube = @hsh_parameters[:thru_nb_tube]
	@thru_mark = @hsh_parameters[:thru_mark]
	@stamp_mark = @hsh_parameters[:stamp_mark]
	@stamp_punch = @hsh_parameters[:stamp_punch]
	@carve_flat = @hsh_parameters[:carve_flat]
	@carve_plan = @hsh_parameters[:carve_plan]
	@cut_in_group = @hsh_parameters[:cut_in_group]
	@cut_out_group = @hsh_parameters[:cut_out_group]
	@surfloc = @hsh_parameters[:surfloc]
	@use_cut_material = @hsh_parameters[:use_cut_material]
end

#PARAMETER: Save the parameters as a model attribute
def parameter_save
	@otpicker.option_update_hash(@hsh_parameters)
	
	@hsh_parameters[:option_recurse] = @option_recurse 
	@hsh_parameters[:option_from_top_parent] = @option_from_top_parent 
	
	@hsh_parameters[:cut_style] = @cut_style 
	@hsh_parameters[:carve_offset] = @carve_offset 
	@hsh_parameters[:thru_no_face] = @thru_no_face 
	@hsh_parameters[:thru_nb_tube] = @thru_nb_tube 
	@hsh_parameters[:thru_mark] = @thru_mark 
	@hsh_parameters[:stamp_mark] = @stamp_mark 
	@hsh_parameters[:stamp_punch] = @stamp_punch 
	@hsh_parameters[:carve_flat] = @carve_flat 
	@hsh_parameters[:carve_plan] = @carve_plan 
	@hsh_parameters[:cut_in_group] = @cut_in_group 
	@hsh_parameters[:cut_out_group] = @cut_out_group 
	@hsh_parameters[:use_cut_material] = @use_cut_material 
	@hsh_parameters[:surfloc] = @surfloc 

	@@hsh_parameters_persist.update @hsh_parameters
	sparam = @@hsh_parameters_persist.inspect.gsub('"', "'")
	Traductor::Default.store @dico_name, @dico_attr, sparam
end

def apply_change_compute	
	@apply_change_enabled = (@geometry_generated && @hsh_param_exec && cut_style_hash != @hsh_param_exec)
end
	
def option_from_top_parent_toggle
	@option_from_top_parent = !@option_from_top_parent
	option_recurse_after
end

def option_recurse_toggle
	@option_recurse = !@option_recurse
	option_recurse_after
end
	
def option_recurse_after
	if @preselection
		selection_update_after_change
		selection_show
		refresh_viewport
		@apply_change_enabled = true if @geometry_generated
	end	
end
	
def cut_style_hash
	mat = (@use_cut_material) ? cut_style_material : :same
	@deviation_stop = MYDEFPARAM[:VHL_OPTION_DeviationStop].degrees
	@deviation_smooth = MYDEFPARAM[:VHL_OPTION_DeviationSmooth].degrees
	{ :cut_style => @cut_style, :thru_no_face => @thru_no_face, :thru_nb_tube => @thru_nb_tube,
      :thru_mark => @thru_mark, :stamp_punch => @stamp_punch, :stamp_mark => @stamp_mark, 
	  :carve_offset => @carve_offset, :cut_in_group => @cut_in_group, :cut_out_group => @cut_out_group, 
	  :carve_flat => @carve_flat, :carve_plan => @carve_plan, :surfloc => @surfloc,
	  :deviation_stop => @deviation_stop, :deviation_smooth => @deviation_smooth, :custom_material => mat }
end

def cut_style_material
	return @custom_material if @custom_material.class == Symbol
	return nil unless @custom_material
	return @custom_material if @custom_material.class == String
	(@custom_material.valid?) ? @custom_material : nil
end

def cut_style_compute_color
	return nil unless @custom_material
	return @custom_material if @custom_material.class == String
	@custom_material.color
end

def cut_style_set(style)
	@cut_style = style
	apply_change_compute
end

def cut_style_modify_carve_offset(val)
	@carve_offset = val
	@cut_style = :carve unless @cut_style == :emboss
	apply_change_compute
end

def cut_style_toggle_thru_no_face
	@thru_no_face = !@thru_no_face
	@cut_style = :thru
	apply_change_compute
end

def cut_style_toggle_thru_single_tube
	@thru_nb_tube = (@thru_nb_tube) ? nil : 1
	@cut_style = :thru
	apply_change_compute
end

def cut_style_toggle_thru_mark
	@thru_mark = !@thru_mark
	@cut_style = :thru
	apply_change_compute
end

def cut_style_toggle_stamp_punch
	@stamp_punch = !@stamp_punch
	@stamp_mark = false if @stamp_punch
	@cut_style = :stamp
	apply_change_compute
end

def cut_style_toggle_stamp_mark
	@stamp_mark = !@stamp_mark
	@stamp_punch = false if @stamp_mark
	@cut_style = :stamp
	apply_change_compute
end
	
def cut_style_toggle_carve_flat
	@carve_flat = !@carve_flat
	@cut_style = :carve unless @cut_style == :emboss
	apply_change_compute
end

def cut_style_toggle_carve_plan
	@carve_plan = !@carve_plan
	@cut_style = :carve unless @cut_style == :emboss
	apply_change_compute
end

def cut_style_toggle_cut_in_group
	@cut_in_group = !@cut_in_group
	@cut_out_group = false if @cut_in_group
	apply_change_compute
end

def cut_style_toggle_cut_out_group
	@cut_out_group = !@cut_out_group
	@cut_in_group = false if @cut_out_group
	apply_change_compute
end
	
def cut_style_unset_group
	return unless @cut_out_group || @cut_in_group
	@cut_out_group = @cut_in_group = false
	apply_change_compute
end
	
def cut_style_toggle_surfloc
	@surfloc = !@surfloc
	apply_change_compute
end
	
#--------------------------------------------------
# Material Settings
#--------------------------------------------------
	
def cut_style_toggle_material
	@use_cut_material = !@use_cut_material
	apply_change_compute
end
	
def select_current_material(mat_spec=nil)
	if mat_spec == :default
		mat = nil
	elsif mat_spec == :current
		mat = @matos.current
	else
		mat = mat_spec
	end
	return if mat == @custom_material
	@custom_material = mat
	@@last_custom_material = mat
	@use_cut_material = true
	@cut_style_color = cut_style_compute_color
	palette_refresh_material
	apply_change_compute
end

def current_material_color
	mat = @matos.current
	(mat) ? mat.color : nil
end

def material_history_navigate(incr, beg_end=false)
	@matos.history_navigate incr, beg_end
	palette_refresh_material
end

def pick_material
	@matpick.picking_start
end
	
#--------------------------------------------------
# Activation / Deactivation
#--------------------------------------------------

#ACTIVATION: Tool activation
def activate
	LibFredo6.register_ruby "VisuHole"
	Traductor::Hilitor.stop_all_active
	@model = Sketchup.active_model
	@model_current = @model if @model != @model_current
	@view_tracker = G6::ViewTracker.new
	@selection = @model.selection
	@entities = @model.active_entities
	@view = @model.active_view
	@ip = Sketchup::InputPoint.new
	@ph = @view.pick_helper
		
	#Initiating the palette
	reset_env

	#Initializing the Executor of Operation
	hsh = { :title => @title, :palette => @palette, :no_commit => true, :end_proc => self.method('geometry_terminate') }
	@suops = Traductor::SUOperation.new hsh

	#Getting the current stencil if any
	@stencil = @stencil_man.current_stencil

	#Handling the initial selection
	selection_initial
		
	@view.invalidate
	show_message
end

#ACTIVATION: Reset the picking environment
def reset_env
	@mode = @otpicker.set_mode(:origin)
	@pt_origin = @pt_target = nil
	@otpicker.reset
	@dragging = false
	@bb_extents = Geom::BoundingBox.new.add @model.bounds
end	

#ACTIVATION: Tool Deactivation
def deactivate(view)
	selection_reset unless @preselection
	parameter_save
	@suops.finish_operation
	view.invalidate
end
			
#ACTIVATION: Exit the tool
def exit_tool
	@aborting = false
	unless @mode == :geometry
		@suops.abort_operation
	end	
	@model.select_tool nil
end

#ACTIVATION: Abort the operation and exit
def abort_tool
	@aborting = true
	@suops.abort_operation
	if @geometry_generated
		text = T6[:T_MSG_ConfirmAbortText] + "\n\n" + T6[:T_MSG_ConfirmAbortQuestion]
		if UI.messagebox(text, MB_YESNO) != 6
			rollback
		end	
	end
	@model.select_tool nil
end

#--------------------------------------------------
# Contextual menu
#--------------------------------------------------

#Contextual menu
def getMenu(menu)
	cxmenu = Traductor::ContextMenu.new
			
	#Face Selection Menu
	if @stencil_man.picking?
		@stencil_man.getMenu cxmenu
		cxmenu.show menu
		return true
	end
	
	#Contribution from origin / target picker
	@otpicker.menu_contribution(cxmenu)
	
	#Abort
	cxmenu.add_sepa
	cxmenu.add_item(@mnu_abort) { abort_tool }

	#Exit
	if @mode == :geometry
		cxmenu.add_sepa
		cxmenu.add_item(@mnu_exit) { exit_tool }
	end
	
	#Showing the menu
	cxmenu.show menu
	true
end

#--------------------------------------------------------------
# VIEWPORT: View Management
#--------------------------------------------------------------

#VIEWPORT: Computing Current cursor
def onSetCursor
	#Palette cursors
	ic = super
	return (ic != 0) if ic
	
	#Cursor managed by the Stencil manager
	return @stencil_man.onSetCursor if @stencil_man.picking?
		
	#Cursor managed by the Stencil manager
	return @matpick.onSetCursor if @matpick.picking?
		
	#Other cursors depending on state
	UI.set_cursor @id_cursor
end

#VIEWPORT: Refresh the viewport
def refresh_viewport
	onSetCursor
	show_message
	@view.invalidate
end
	
#VIEWPORT: Show message in the palette
def show_message
	#Mouse if either in the palette or out of the viewport
	if @mouseOut
		@palette.set_message nil
		return
	elsif @mouse_in_palette
		@palette.set_message @palette.get_main_tooltip, 'aliceblue'
		return
	elsif @stencil_man.picking?
		@stencil_man.show_message
		return
	elsif @matpick.picking?
		@matpick.show_message
		return
	else	
		@palette.set_message nil unless @geometry_generated
	end	
	
	text = label = value = ""
	if @mode == :origin
		if @pt_origin && @repere_axes && @stencil_man.current_stencil
			text = @msg_pick_origin
		else
			text = @msg_double_click_exit
		end	
	elsif @mode == :target
		text = @msg_pick_target
	end	
	
	#Showing the message in the status bar
	Sketchup.set_status_text label, SB_VCB_LABEL
	Sketchup.set_status_text value, SB_VCB_VALUE
	Sketchup.set_status_text text	
end

#---------------------------------------------------------------------------------------------
# MOVE: Mouse Movement Methods
#---------------------------------------------------------------------------------------------

#MOVE: Mouse Movements
def onMouseMove_zero(flag=nil) ; onMouseMove(@flags, @xmove, @ymove, @view) if @xmove ; end
def onMouseMove(flags, x, y, view)
	
	#Event for the palette
	@xmove = x
	@ymove = y
	if super
		@mouse_in_palette = true
		repere_reset
		refresh_viewport
		return
	end	
	@mouse_in_palette = false
	
	#Synchronize draw and move
	return if @moving

	#Stencil picking mode
	if @stencil_man.picking?
		@stencil_man.onMouseMove(flags, x, y, view)
		
	#Material picking mode
	elsif @matpick.picking?
		@matpick.onMouseMove(flags, x, y, view)
		
	#No current stencil
	elsif !@stencil_man.current_stencil
		#do nothing
		
	#Picking pseudo solid	
	else
		#Pick origin with inference
		if @mode == :origin
			@origin_info = @otpicker.origin_pick view, x, y, false, @shift_down, true
			@pt_origin, @type_origin = @origin_info
			@bb_extents.add @pt_origin if @pt_origin
			compute_actual_face
			selection_dynamic
			repere_compute_plane
			
		#Tracking the target	
		elsif @mode == :target
			@target_info = @otpicker.target_pick(view, x, y, false)
			@pt_target, = @target_info
			repere_dragging if @pt_origin != @pt_target

			#Updating the Bounding box for drawing
			@bb_extents.add @pt_target if @pt_target		
		end
	end
	
	#Refreshing the view
	@moving = true
	refresh_viewport
end	

def onMouseLeave(view)
	@mouseOut = true
	repere_reset
	show_message
	view.invalidate
end

def onMouseEnter(view)
	@mouseOut = false
	view.invalidate
end
	
#MOVE: Before Zooming or changing view
def suspend(view)
	@keyman.reset
	@zoom_void.suspend(view, @xmove, @ymove)
end

#MOVE: After changing view
def resume(view)
	@keyman.reset
	@zoom_void.resume(view)
	refresh_viewport
end
	
#VIEW: Notification of view changed
def onViewChanged(view)

end
	
#---------------------------------------------------------------------------------------------
# CLICK: Click Management
#---------------------------------------------------------------------------------------------

#CLICK: Button click DOWN
def onLButtonDown(flags, x, y, view)
	#Interrupting geometry construction if running
	return if @suops.interrupt?
	
	#Palette management
	if super
		refresh_viewport
		return
	end
	
	#Storing the reference
	@button_down = true
	@xdown = x
	@ydown = y	
	
	#Stencil mode
	if @stencil_man.picking?
		@stencil_man.onLButtonDown(flags, x, y, view)
		refresh_viewport
		
	#Stencil mode
	elsif @matpick.picking?
		@matpick.onLButtonDown(flags, x, y, view)
		refresh_viewport

	#NO current stencil
	elsif !@stencil_man.current_stencil
		#do nothing
		
	#Solid mode
	else	
		if @mode == :origin
			onMouseMove_zero unless @pt_origin
			repere_start
		elsif @mode == :target
			repere_stop
		end	
	end	
	@time_down_start = Time.now
end

#CLICK: Button click UP - Means that we end the selection
def onLButtonUp(flags, x, y, view)
	if @ignore_up
		@ignore_up = false
		return
	end	
	@ignore_doubleclick = false
	
	#Palette buttons
	if super
		@time_down_start = nil
		onMouseMove_zero
		return
	end
	return unless @button_down
	@button_down = false
	
	#Logic for dragging
	if @stencil_man.picking?
		@stencil_man.onLButtonUp(flags, x, y, view)
		refresh_viewport
	elsif @matpick.picking?
		@matpick.onLButtonUp(flags, x, y, view)
		refresh_viewport
	elsif !@stencil_man.current_stencil
		#do nothing
	else
		far_from_origin = (@xdown - x).abs > 3 || (@ydown - y).abs > 3
		if @time_down_start && (Time.now - @time_down_start) > @duration_long_click && !far_from_origin
			reset_env
			onMouseMove_zero
			@otpicker.direction_pick_from_model
		elsif @dragging
			if @time_down_start && (Time.now - @time_down_start) > 0.3 && ((@xdown - x).abs > 1 || (@ydown - y).abs > 1)
				repere_stop
			end
		end	
	end	
end

#CLICK: Double Click received
def onLButtonDoubleClick(flags, x, y, view)
	return if super
	return if @ignore_doubleclick
	
	if @stencil_man.picking?
		@stencil_man.onLButtonDoubleClick(flags, x, y, view)
	elsif @matpick.picking?
		@matpick.onLButtonDoubleClick(flags, x, y, view)
	elsif !@stencil_man.current_stencil
		#do nothing
	else
		if @type_origin == :void_origin
			exit_tool
		else
			repere_stop
		end	
	end	
end

#---------------------------------------------------------------------------------------------
# KEY: Keyboard handling
#---------------------------------------------------------------------------------------------

#Return key pressed
def onReturn(view)
	if @stencil_man.picking?
		@stencil_man.onReturn(view)
	elsif @matpick.picking?
		@matpick.onReturn(view)
	elsif @apply_change_enabled
		apply_change
	else	
		@otpicker.direction_pick_from_model
	end
	refresh_viewport	
end

#KEY: Manage key events
def key_manage(event, key, view)
	#Picking mode in Stencil Manager
	if @stencil_man.picking?
		@stencil_man.key_manage(event, key, view)
		refresh_viewport
		return
	end	

	case event
	
	#SHIFT key
	when :shift_down
		@otpicker.direction_changed
	when :shift_toggle
	
	when :shift_up
		
	#CTRL key	
	when :ctrl_down	
		onMouseMove_zero

	when :ctrl_up
		onMouseMove_zero

	when :ctrl_other_up
	
	#ALT key
	when :alt_down
		@otpicker.no_inference_toggle
	when :alt_up	
		@otpicker.no_inference_set false
	
	#Arrows	
	when :arrow_down	
		action_from_arrow key
		
	#Backspace	
	when :backspace
		@otpicker.inference_reset

	#TAB	
	when :tab
		@stencil_man.picking_toggle
		
	when :shift_tab
		@stencil_man.history_navigate -1
	end
end

#KEY: Handle Key down
def onKeyDown(key, rpt, flags, view)
	if @stencil_man.picking?
		ret_val = @stencil_man.onKeyDown(key, rpt, flags, view)
	else
		ret_val = @keyman.onKeyDown(key, rpt, flags, view)
	end	
	onSetCursor
	@view.invalidate
	ret_val
end

#KEY: Handle Key up
def onKeyUp(key, rpt, flags, view)
	if @stencil_man.picking?
		ret_val = @stencil_man.onKeyUp(key, rpt, flags, view)
	else
		ret_val = @keyman.onKeyUp(key, rpt, flags, view)
	end	
	onSetCursor
	@view.invalidate
	ret_val
end

#KEY: Manage parameters from the arrow keys
def action_from_arrow(key)	
	#Computing the new code
	case key
	when VK_UP
		code = :follow_z
	when VK_LEFT
		code = :follow_y
	when VK_RIGHT
		code = :follow_x
	when VK_DOWN
		code = :no
	end
	
	@otpicker.option_planar_set_dir code
	refresh_viewport
end	

#---------------------------------------------------------------------------------------------
# DRAW: Drawing Methods
#---------------------------------------------------------------------------------------------

#DRAW: Draw top method
def draw(view)	
	if @stencil_man.picking?
		@stencil_man.draw view
	elsif @matpick.picking?
		@matpick.draw view
	elsif !@stencil_man.current_stencil
		draw_message_pick_stencil view
	else
		draw_selection view
		unless @mouse_in_palette || @mouseOut
			draw_stencil view
			draw_repere view
			@otpicker.draw_all view
		end	
	end	
	@moving = false
	
	#Drawing the palette
	super
end

#DRAW: Draw a message to pick a stencil first
def draw_message_pick_stencil(view)
	return unless @xmove
	G6.draw_rectangle_text(view, @xmove, @ymove, @msg_no_stencil, 'pink', 'red')
	
	init_instructions_no_stencil unless @no_stencil_instructions
	t = Geom::Transformation.translation(Geom::Vector3d.new(@xmove + 20, @ymove, 0)) * @tr_no_stencil_instructions
	@ogl.process_draw_GL(view, t, @no_stencil_instructions)
end

#DRAW: Draw the axes of the repere
def draw_repere(view)
	return unless @pt_origin && @repere_axes 
	size = view.pixels_to_model 70, @pt_origin
	view.line_width = 2
	view.line_stipple = ''
	vecx, vecy, normal = @repere_axes
	[[normal, 'blue'], [vecx, 'red'], [vecy, 'green']].each do |vec, color|
		view.drawing_color = color
		pt2 = @pt_origin.offset vec, size
		view.draw2d GL_LINE_STRIP, [@pt_origin, pt2].collect { |pt| view.screen_coords pt }
		view.line_width = 1
	end	
end

#DRAW: Draw the stencil footprint in 2D
def draw_stencil(view)
	return unless @pt_origin && @repere_axes && @repere_tr
	stencil = @stencil_man.current_stencil
	return unless stencil
	stencil.draw_flat2d view, @repere_tr, @color_stencil, 'black', 1, ''
end

#DRAW: Draw the pseudo selection for components and groups
def draw_selection(view)
	return if !@selection_box_lines || @selection_box_lines.empty?
	view.line_width = 2
	view.line_stipple = ''
	view.drawing_color = @su_selection_color
	view.draw GL_LINES, @selection_box_lines.collect { |pt| G6.small_offset(view, pt) }
end

#DRAW: return the extents for drawing
def getExtents
	@bb_extents
end

#----------------------------------------------------------------------------------------------------
# NOTIFY: Call back from Otpicker
#----------------------------------------------------------------------------------------------------

#NOTIFY: Notification from the Origin-Target picker
def notify_from_otpicker(event)
	case event
	when :shift_down
		@keyman.shift_down?
	when :ctrl_down	
		@keyman.ctrl_down?
	when :onMouseMove_zero
		onMouseMove_zero
	when :plane
		repere_compute_plane
	else
		:no_event
	end	
end

#NOTIFY: Notify from Stencil manager
def notify_from_stencil_manager(event)
	case event
	when :enter_picking
		@palette.turn_off
		selection_suspend
	
	when :exit_picking
		@ignore_doubleclick = true
		reset_env
		selection_resume
		@palette.turn_on
		onMouseMove_zero
		UI.start_timer(0.5) { reset_clicking }
		
	when :stencil
		UI.start_timer(0) { onMouseMove_zero }
		@apply_change_enabled = true if @geometry_generated
		
	else
		:no_event
	end	
end

#NOTIFY: Notify from Stencil manager
def notify_from_material_picker(event)
	case event
	when :enter_picking
		@palette.turn_off
		selection_suspend
	
	when :exit_picking
		@ignore_doubleclick = true
		@ignore_up = true
		reset_env
		selection_resume
		@palette.turn_on
		onMouseMove_zero
		UI.start_timer(0.5) { reset_clicking }
		
	when :material
		mat = @matpick.picked_material
		select_current_material mat
		UI.start_timer(0) { onMouseMove_zero }
		
	else
		:no_event
	end	
end

def reset_clicking
	@ignore_up = false
	@ignore_doubleclick = false
end

#---------------------------------------------------------------------------------------------
# REPERE: Manage the axes and coordinate framework for projection
#---------------------------------------------------------------------------------------------

#ACTION: Start either Move or Rotate
def repere_start
	@mode = @otpicker.set_mode(:target)
	refresh_viewport
end

#ACTION: Stop either Move or Rotate
def repere_stop
	repere_finish
end

#PENCIL: Store current information
def repere_finish
	@otpicker.vcb_freeze
	@action_stopped = true
	repere_compute_plane
	algo_execute
	reset_env
end

def repere_dragging
	@action_stopped = false
	repere_compute_plane
	@dragging = true
end

def repere_reset
	@repere_axes = nil
	@repere_tr = nil
end

#REPERE: Compute the axes of the plane
def repere_compute_plane
	return unless @otpicker
	@repere_axes = nil
	#puts "\nNORMAL HOLE = #{normal}" if normal
	#normal = nil
	normal = @otpicker.direction_plane_active
	normal = @normal_hole unless normal
	#normal2 = @otpicker.direction_plane_active
	#puts "NORMAL ACTIVE = #{normal2}"
	return unless normal && @pt_origin
	
	#Reversing the normal to make it emerging from face
	pt2d = @view.screen_coords @pt_origin
	ray = @view.pickray pt2d.x, pt2d.y
	if (normal % ray[1] > 0)
		normal = normal.reverse
	end	
	
	#Computing the axes
	if @pt_target && @pt_target != @pt_origin
		pt = ORIGIN.offset(@pt_origin.vector_to(@pt_target)).project_to_plane [ORIGIN, normal]
		vecx = ORIGIN.vector_to(pt)
	end	
	vecx = normal.axes[0] unless vecx
	vecy = normal * vecx
	vecx, vecy = normal.axes unless vecy.valid?
	@repere_axes = [vecx, vecy, normal]	
	@repere_tr = Geom::Transformation.axes @pt_origin, *@repere_axes
end

def oldrepere_compute_plane
	return unless @otpicker
	@repere_axes = nil
	normal = @normal_hole
	puts "\nNORMAL HOLE = #{normal}" if normal
	normal = nil
	normal = @otpicker.direction_plane_active unless normal
	normal2 = @otpicker.direction_plane_active
	puts "NORMAL ACTIVE = #{normal2}"
	return unless normal && @pt_origin
	
	#Reversing the normal to make it emerging from face
	pt2d = @view.screen_coords @pt_origin
	ray = @view.pickray pt2d.x, pt2d.y
	if (normal % ray[1] > 0)
		normal = normal.reverse
	end	
	
	#Computing the axes
	if @pt_target && @pt_target != @pt_origin
		pt = ORIGIN.offset(@pt_origin.vector_to(@pt_target)).project_to_plane [ORIGIN, normal]
		vecx = ORIGIN.vector_to(pt)
	end	
	vecx = normal.axes[0] unless vecx
	vecy = normal * vecx
	vecx, vecy = normal.axes unless vecy.valid?
	@repere_axes = [vecx, vecy, normal]	
	@repere_tr = Geom::Transformation.axes @pt_origin, *@repere_axes
end

#--------------------------------------------------------------
#--------------------------------------------------------------
# SELECTION: Manage selection
#--------------------------------------------------------------
#--------------------------------------------------------------

#SELECTION: Reset the selection
def selection_reset
	@selection_info = []
	@selection_box_lines = []
	@selection.clear
end

#SELECTION: suspend the selection for another mode
def selection_suspend
	@selection.clear
end

#SELECTION: suspend the selection for another mode
def selection_resume
	@selection.clear
	selection_show if @preselection		
end

#SELECTION: Register an element of selection
def selection_register_element(comp, tr)
	return if comp.class == Array && comp.empty?
	@selection_info.push [comp, tr]
	unless comp.class == Array
		@selection_box_lines += G6.grouponent_box_lines(@view, comp, tr)
	end	
end

#SELECTION: Show the selection in the model
def selection_show	
	lst_elt = @selection_info.find_all { |comp, tr| comp.class == Array }
	@selection.add lst_elt.collect { |comp, tr| comp }
end
	
#SELECTION: Update the selection after options have been changed	
def selection_update_after_change
	if @preselection
		@selection.clear
		@selection.add @preselection
		selection_initial
	else
		onMouseMove_zero
	end	
end
	
#SELECTION: Analyze the initial selection	
def selection_initial
	@selection_info = []
	@selection_box_lines = []
	return if @selection.empty?
	selection = @selection.to_a
	
	#Faces at top level
	selection_register_element selection.grep(Sketchup::Face), @tr_id
	
	#Groups and Component Instances at top level
	(selection.grep(Sketchup::Group) + selection.grep(Sketchup::ComponentInstance)).each do |e|
		selection_register_element e, e.transformation
	end	
	
	#No valid preselection
	return if @selection_info.empty?
	
	#Recursing the selection if required
	@preselection = @selection.to_a
	unless @preselection.empty?
		selection_recurse if @option_recurse || !@preselection.find { |e| e.instance_of?(Sketchup::Face) }
	end
	
	#Showing the selection
	selection_show
end

#SELECTION: Compute the actual face under the cursor at any level
def compute_actual_face
	pt, type, info_comp = @origin_info
	elt, comp, tr = info_comp
	
	@normal_hole = nil
	info_face = @otpicker.origin_face_info
	if info_face
		face, comp, tr = info_comp = info_face
		@origin_info = [pt, type, [face, comp, tr]]
		@normal_hole = G6.transform_vector(face.normal, tr)
	end	
end

#SELECTION: Analyse the dynamic selection from the origin picked
def selection_dynamic
	return if @preselection
	selection_reset
	
	#Information on picked origin
	ctrl_down = @keyman.ctrl_down?
	pt, type, info_comp = @origin_info
	elt, comp, tr = info_comp
	
	#Finding the apporpiate component / group
	elt0 = top_comp_info = nil
	if comp
		if @option_from_top_parent && !ctrl_down
			pt, top_parent = @otpicker.origin_info_top_parent
			top_comp_info = [top_parent, top_parent.transformation] if top_parent
		else
			top_comp_info = [comp, tr] if comp
		end
	
	elsif elt.instance_of?(Sketchup::Face) || elt.instance_of?(Sketchup::Edge)
		elt0 = elt
	elsif elt.instance_of?(Sketchup::Vertex)
		elt0 = elt.edges[0]
	else
		return
	end	
	
	#Handling components
	if top_comp_info
		selection_register_element *top_comp_info			
	elsif elt0
		if ctrl_down
			if elt0.instance_of?(Sketchup::Face)
				faces = G6.face_neighbours elt0
			else
				faces = elt0.faces
				return if faces.empty?
			end	
		else
			faces = elt0.all_connected.grep(Sketchup::Face)
		end	
		selection_register_element faces, tr 
	end
	
	#Showing the selection
	selection_recurse if !ctrl_down && (@option_recurse || @option_from_top_parent)
	selection_show	
end

#SELECTION: Recurse the current selection
def selection_recurse
	#return unless @option_recurse || @option_from_top_parent
	@selection_info.each do |comp, tr|
		next if comp.class == Array
		selection_recurse_element(G6.grouponent_entities(comp), tr)
	end	
end

#SELECTION: Recurse the current selection
def selection_recurse_element(entities, tr)
	groups = entities.grep(Sketchup::Group)
	groups.each do |group|
		t = tr * group.transformation
		selection_register_element group, t
		selection_recurse_element(group.entities, t)
	end	
	
	comps = entities.grep(Sketchup::ComponentInstance)
	comps.each do |comp|
		t = tr * comp.transformation
		selection_register_element comp, t
		selection_recurse_element(comp.definition.entities, t)
	end	
end

#SELECTION: Save the current selection based on entityIDs for toplevel geometry
def selection_save
	@selection_info_prev = []
	@selection_ids = nil
	@selection_info.each do |comp, tr|
		if comp.class == Array
			@selection_ids = comp.collect { |e| e.entityID }
		end	
		@selection_info_prev.push [comp, tr]
	end
end

#SELECTION: Save the current selection based on entityIDs for toplevel geometry
def selection_restore
	@selection_info = @selection_info_prev
	
	return unless @selection_ids
	
	hmap = {}
	@entities.grep(Sketchup::Face).each { |f| hmap[f.entityID] = f }
		
	@selection_info.each_with_index do |a, i|
		comp, tr = a
		if comp.class == Array
			lfaces = []
			@selection_ids.each do |id|
				f = hmap[id]
				lfaces.push f if f
			end	
			@selection_info[i] = [lfaces, tr]
			break
		end	
	end
end

#---------------------------------------------------------------------------------------
# UNDO: Undo and Rollback Management
#---------------------------------------------------------------------------------------

#UNDO: Cancel and undo methods
def onCancel(flag, view)
	#Interrupting geometry construction if running
	return if @suops.interrupt?
	
	return @stencil_man.picking_toggle if @stencil_man.picking?
	return @matpick.picking_stop if @matpick.picking?
	case flag
	when 1, 2	#Undo or reselect the tool
		handle_undo
	when 0		#user pressed Escape	
		handle_escape
	end
end

#UNDO: Handle an undo
def handle_undo
	reset_env
	selection_reset
	@geometry_generated = false
	@apply_change_enabled = false
	UI.start_timer(0.1) { rollback }
end

#UNDO: Handle the escape key depending on current mode
def handle_escape
	reset_env if @dragging
	if @preselection
		selection_reset
		@preselection = false
	end	
	onMouseMove_zero
end

def rollback
	Sketchup.undo if @geometry_generated
	@geometry_generated = false
	@apply_change_enabled = false
	if @preselection
		selection_restore
		@selection_info.each do |comp, tr|
			@selection.add comp
		end	
	end
end

#--------------------------------------------------------------
#--------------------------------------------------------------
# ALGO: Algorithm
#--------------------------------------------------------------
#--------------------------------------------------------------

#ALGO: Launch the intersection
def algo_execute
	UI.set_cursor @id_cursor_hourglass_red	

	#Getting the stencil
	stencil = @stencil_man.current_stencil

	#Saving the selection with entityID to restore it later in case of cancel
	selection_save
	
	#Parameters for the operation
	@repere_tr_prev = @repere_tr
	hsh = { :repere_tr => @repere_tr, :stencil => stencil, :suops => @suops }
	@hsh_param_exec = cut_style_hash
	
	#Performing the intersection asynchonously
	@intersector.top_execute @selection_info, hsh, @hsh_param_exec
end

def geometry_terminate(time)
	#Getting the status of execution
	status = @intersector.get_status_execution
	
	#Heavy error
	if status.is_a?(Exception)
		Traductor::RubyErrorDialog.invoke status, @title, T6[:ERR_Geometry]
		@geometry_generated = false
		abort_tool
		return
	end	
	
	#Intersection created or not
	@geometry_generated = status
	@apply_change_enabled = false
	
	#Committing the operation
	if @geometry_generated	
		@suops.commit_operation
	else	
		@suops.abort_operation	
	end
	
	#Resetting the selection
	selection_reset
	#@preselection = nil
end

	
#ALGO: Relaunch the intersection after a change, with a Undo first	
def apply_change
	Sketchup.undo
	@repere_tr = @repere_tr_prev
	selection_restore
	algo_execute
end
	
#--------------------------------------------------------------
#--------------------------------------------------------------
# PALETTE: Palette Management
#--------------------------------------------------------------
#--------------------------------------------------------------

#---------------------------------------------------------------------
# Creation of the palette
#---------------------------------------------------------------------

#PALETTE: Separator for the Main and floating palette
def pal_separator ;	@palette.declare_separator ; end

#PALETTE: Initialize the main palette and top level method
def init_palette
	hshpal = { :width_message => 0, :width_message_min => 150, :key_registry => :toposhaper }
	@palette = Traductor::Palette.new hshpal
	@draw_local = self.method "draw_button_opengl"
	@blason_proc = self.method "draw_button_blason"
	@symb_blason = :blason
	@bk_color_tool = "lemonchiffon"
	@info_text1 = ""
	@info_text2 = ""
	@info_message = ""
		
	#Blason Button
	tip = "VisuHole" + ' ' + T6[:T_STR_DefaultParamDialog]
	hsh = { :blason => true, :draw_proc => @blason_proc, :tooltip => tip }
	@palette.declare_button(@symb_blason, hsh) { MYPLUGIN.invoke_default_parameter_dialog }

	#Stencil
	palette_stencil
	
	#Plane and Vector settings
	pal_separator
	hsh = { :passive_title => true }
	@otpicker.palette_contribution_planar @palette, hsh
	
	#Selection option
	palette_selection
	
	#Cut style
	palette_cut_style
	
	#Apply change
	palette_apply_change
		
	#Abort and Exit
	pal_separator
	hsh = { :draw_proc => :std_abortexit, :main_color => 'red', :frame_color => 'green', 
	        :tooltip => T6[:T_STR_AbortTool] }
	@palette.declare_button(:pal_abort, hsh) { abort_tool }

	#Rollback
	hsh = { :tooltip_proc => T6[:T_STR_UndoCtrlZ], :draw_proc => :rollback }
	@palette.declare_button(:pal_back, hsh) { rollback }
	
	#Exit tool
	hsh = { :draw_proc => :std_exit, :tooltip => T6[:T_STR_ExitTool] }
	@palette.declare_button(:pal_exit, hsh) { exit_tool }

	#Associating the palette
	set_palette @palette
end

#PALETTE: Pick a Stencil in model
def palette_stencil
	pal_separator
	hsh_keys = { :pick => "[#{T6[:T_KEY_TAB]}]", :nav => "[#{T6[:T_KEY_Shift]}-#{T6[:T_KEY_TAB]}]" }
	@stencil_man.palette_button_pick_shape @palette, hsh_keys
	@stencil_man.palette_button_show_stencil @palette
end

#PALETTE: Pick a Stencil in model
def palette_surfloc
	pal_separator
	value_proc = proc { @surfloc }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_Surfloc], :draw_proc => @draw_local }
	@palette.declare_button(:pal_surfloc, hsh) { cut_style_toggle_surfloc }
end

#PALETTE: Pick a Stencil in model
def palette_cut_style
	pal_separator

	#Frilling Thru
	palette_cut_style_thru

	#Stamp
	palette_cut_style_stamp
	
	#Carve / Emboss
	palette_cut_style_carve
	
	#Surface location
	palette_surfloc
		
	#Group options
	palette_cut_in_out_group
	
	#Cut material
	palette_cut_material
end

#PALETTE: In-site and Out-site Group option
def palette_cut_in_out_group	
	pal_separator
	h = 24
	w = 24
	hgt = 8

	symb_master = :pal_cut_style_group
	passive_proc_title = proc { !@cut_in_group && !@cut_out_group }
	val_proc_title = proc { @cut_in_group || @cut_out_group }
	hsh = { :type => 'multi_free', :tooltip => T6[:TIP_NoGroup] = "NO Grouping", :height => hgt, :bk_color => @color_but_tit,
			:hi_color => @color_but_hi, :value_proc => val_proc_title, :bk_color => 'lightblue',
			:passive => passive_proc_title }
	@palette.declare_button(symb_master, hsh) { cut_style_unset_group }

	hshp = { :parent => symb_master, :height => h, :width => w, :hi_color => @color_but_hi}
	
	value_proc = proc { @cut_in_group }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_CutInGroup], :main_color => 'gray', :draw_proc => [:std_group, @draw_local] }
	@palette.declare_button(:pal_cut_style_cut_in_group, hshp, hsh) { cut_style_toggle_cut_in_group }
	
	value_proc = proc { @cut_out_group }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_CutOutGroup], :main_color => 'green', :draw_proc => :std_group }
	@palette.declare_button(:pal_cut_style_cut_out_group, hshp, hsh) { cut_style_toggle_cut_out_group }
	
end

#PALETTE: Carve / Emboss
def palette_cut_style_carve	
	pal_separator
	tx_carve = T6[:BOX_StampCarve]
	tx_emboss = T6[:BOX_StampEmboss]
	w1, = G6.simple_text_size tx_carve
	w2, = G6.simple_text_size tx_emboss
	w = [w1, w2].max + 16
	h = 16
	wsepa = 8
	wflat = 30
	wplan = wflat
	wfield = 90
	wtop = 2 * w + wsepa
	wbot = wflat + wplan + wfield + wsepa
	if wtop < wbot
		w = (wbot - wsepa) / 2
	else
		wfield = wtop - wflat - wplan - wsepa
	end	
	
	hshc = { :width => w, :height => h, :hi_color => @color_but_hi, :bk_color => @color_but_tit }
	
	value_proc = proc { @cut_style == :carve }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_StampCarve], :text => tx_carve, :rank => 1 }
	@palette.declare_button(:pal_cut_style_carve, hshc, hsh) { cut_style_set :carve }
	
	hsh = { :passive => true, :draw_proc => :separator_V, :rank => 1, :width => wsepa }
	@palette.declare_button(:pal_cut_style_carve_sepa1, hshc, hsh)

	value_proc = proc { @cut_style == :emboss }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_StampEmboss], :text => tx_emboss, :rank => 1 }
	@palette.declare_button(:pal_cut_style_emboss, hshc, hsh) { cut_style_set :emboss }

	#Flat option
	hshc = { :width => wflat, :height => h, :hi_color => @color_but_hi_s }
	value_proc = proc { @carve_flat }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_StampFlat], :draw_proc => @draw_local }
	@palette.declare_button(:pal_cut_style_flat, hshc, hsh) { cut_style_toggle_carve_flat }

	#Planar option
	hshc = { :width => wplan, :height => h, :hi_color => @color_but_hi_s }
	value_proc = proc { @carve_plan }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_StampKeepPlane], :draw_proc => @draw_local }
	@palette.declare_button(:pal_cut_style_plan, hshc, hsh) { cut_style_toggle_carve_plan }

	#Input field for offset
	hsh = { :passive => true, :draw_proc => :separator_V, :width => wsepa }
	@palette.declare_button(:pal_cut_style_carve_sepa3, hshc, hsh)

	prompt = T6[:PROMPT_StampOffset]
	tip = T6[:TIP_StampOffset]
	hshi = { :vtype => :len, :vprompt => prompt }
	get_proc = proc { @carve_offset }
	set_proc = proc { |val| cut_style_modify_carve_offset val }
	show_proc = proc { |val| Sketchup.format_length val }
	input = Traductor::InputField.new hshi, { :get_proc => get_proc, :set_proc => set_proc, :show_proc => show_proc }
	
	hsh = { :bk_color => 'lightblue', :input => input, :tooltip => tip, :height => h, :width => wfield }
	@palette.declare_button :pal_cut_style_carve_offset, hsh 
end

#PALETTE: Stamp
def palette_cut_style_stamp
	pal_separator
	
	hgt = 16
	title = T6[:BOX_Stamp]
	w, = G6.simple_text_size title
	w += 10
	w = [w, 70].max
	widbut = w / 2
	
	symb_master = :pal_cut_style_stamp
	val_proc_title = proc { @cut_style == :stamp }
	hsh = { :type => 'multi_free', :text => title, :tooltip => T6[:TIP_StampTitle], :height => hgt, :bk_color => @color_but_tit,
			:hi_color => @color_but_hi, :value_proc => val_proc_title }
	@palette.declare_button(symb_master, hsh) { cut_style_set :stamp }
		
	#Creating the buttons
	hshp = { :parent => symb_master, :height => hgt, :width => widbut, :hi_color => @color_but_hi_s, :draw_proc => @draw_local}
	
	value_proc = proc { @stamp_punch }
	hsh = { :tooltip => T6[:TIP_StampPunch], :value_proc => value_proc  }
	@palette.declare_button("#{symb_master}_stamp_punch".intern, hshp, hsh) { cut_style_toggle_stamp_punch }

	value_proc = proc { @stamp_mark }
	hsh = { :tooltip => T6[:TIP_StampMark], :value_proc => value_proc  }
	@palette.declare_button("#{symb_master}__stamp_mark".intern, hshp, hsh) { cut_style_toggle_stamp_mark }
end

#PALETTE: Thru
def palette_cut_style_thru
	pal_separator
	
	hgt = 16
	title = T6[:BOX_DrillThru]
	w, = G6.simple_text_size title
	w += 10
	w = [w, 90].max
	widbut = w / 3
	
	symb_master = :pal_cut_style_thru
	val_proc_title = proc { @cut_style == :thru }
	hsh = { :type => 'multi_free', :text => title, :tooltip => T6[:TIP_ThruTitle], :height => hgt, :bk_color => @color_but_tit,
			:hi_color => @color_but_hi, :value_proc => val_proc_title }
	@palette.declare_button(symb_master, hsh) { cut_style_set :thru }
		
	#Creating the buttons
	hshp = { :parent => symb_master, :height => hgt, :width => widbut, :hi_color => @color_but_hi_s, :draw_proc => @draw_local}
	
	value_proc = proc { @thru_nb_tube == 1 }
	hsh = { :tooltip => T6[:TIP_ThruSingleTube], :value_proc => value_proc  }
	@palette.declare_button("#{symb_master}__single_tube".intern, hshp, hsh) { cut_style_toggle_thru_single_tube }

	value_proc = proc { @thru_no_face }
	hsh = { :tooltip => T6[:TIP_ThruNoFace], :value_proc => value_proc  }
	@palette.declare_button("#{symb_master}__no_face".intern, hshp, hsh) { cut_style_toggle_thru_no_face }
	
	value_proc = proc { @thru_mark }
	hsh = { :tooltip => T6[:TIP_ThruMark], :value_proc => value_proc  }
	@palette.declare_button("#{symb_master}__mark".intern, hshp, hsh) { cut_style_toggle_thru_mark }
end

#PALETTE: Thru
def palette_cut_material
	pal_separator
	
	hgt = 16
	title = T6[:T_STR_TXT_Material]
	w, = G6.simple_text_size title
	widpick = 32
	widnav = 16
	widcur = 20
	wsepa = 12
	w = [widpick + 2 * widnav + 2 * widcur + wsepa, w + widcur].max
	widcur = 0.5 * (w - 2 * widnav - widpick - wsepa)
	
	@symb_master_mat = :pal_cut_material
	tip_proc = proc { palette_tip_custom_material } 
	val_proc_title = proc { @use_cut_material }
	hsh = { :type => 'multi_free', :text => title, :tip_proc => tip_proc, :height => hgt, :bk_color => @color_but_tit_n,
			:hi_color => @color_but_hi, :value_proc => val_proc_title, :justif => 'LH', :draw_proc => @draw_local }
	@palette.declare_button(@symb_master_mat, hsh) { cut_style_toggle_material }
		
	#Creating the buttons
	hshp = { :parent => @symb_master_mat, :height => hgt, :draw_proc => @draw_local}

	symb = "#{@symb_master_mat}__prev".intern
	hsh = { :tooltip => T6[:T_TIP_PrevMaterial], :width => widnav, :long_click_proc => (proc { material_history_navigate -1, true }) }
	@palette.declare_button(symb, hshp, hsh) { material_history_navigate(-1) }
	
	@symb_cur_mat = "#{@symb_master_mat}__cur_material".intern
	tip_proc = proc { palette_tip_current_material } 
	hsh = { :tip_proc => tip_proc, :width => widcur }
	@palette.declare_button(@symb_cur_mat, hshp, hsh) { select_current_material :current }
	
	symb = "#{@symb_master_mat}__next".intern
	hsh = { :tooltip => T6[:T_TIP_NextMaterial], :width => widnav, :long_click_proc => (proc { material_history_navigate +1, true }) }
	@palette.declare_button(symb, hshp, hsh) { material_history_navigate(1) }
	
	symb = "#{@symb_master_mat}__def_material".intern
	hsh = { :tooltip => T6[:T_TXT_DefaultMaterial], :width => widcur }
	@palette.declare_button(symb, hshp, hsh) { select_current_material :default }
	
	symb = "#{@symb_master_mat}__sepa".intern
	hsh = { :passive => true, :width => wsepa, :draw_proc => :separator_V }
	@palette.declare_button(symb, hshp, hsh)
	
	hsh = { :tooltip => T6[:T_TIP_PickMaterial], :width => widpick, :draw_proc => :std_sample_material }
	@palette.declare_button("#{@symb_master_mat}__pick_material".intern, hshp, hsh) { pick_material }
end

#PALETTE: Refresh the material buttons
def palette_refresh_material
	@palette.button_refresh_draw(@symb_master_mat)
	@palette.button_refresh_draw(@symb_cur_mat)
end

#PALETTE: Tooltip method for Current model material button
def palette_tip_current_material
	mat = @matos.current
	name = (mat) ? mat.display_name : T6[:T_TXT_DefaultMaterial] 
	name += " - #{T6[:T_STR_TXT_Textured]}" if mat && mat.texture
	T6[:T_TIP_CurrentMaterial] + " [#{name}]"
end

#PALETTE: Tooltip method for custom material button
def palette_tip_custom_material
	tip = T6[:TIP_CustomMaterial]
	if !@use_cut_material || @custom_material == :same
		name = T6[:TIP_NoUseMaterial]
	elsif @custom_material.class == String
		name = @custom_material
	elsif @custom_material && @custom_material.valid?
		name = @custom_material.display_name
		name += " - #{T6[:T_STR_TXT_Textured]}" if @custom_material.texture
	else
		name = T6[:T_TXT_DefaultMaterial]
	end	
	T6[:TIP_CustomMaterial] + " [#{name}]"
end

#PALETTE: Pick a Stencil in model
def palette_selection
	pal_separator
	
	value_proc = proc { @option_from_top_parent }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_SelectTopParent], :draw_proc => :std_selection_comp_from_top }
	@palette.declare_button(:pal_from_top_parent, hsh) { option_from_top_parent_toggle }
	
	value_proc = proc { @option_recurse }
	hsh = { :value_proc => value_proc, :tooltip => T6[:TIP_SelectRecurse], :draw_proc => :std_selection_comp_recurse }
	@palette.declare_button(:pal_recurse, hsh) { option_recurse_toggle }
end

#PALETTE: Pick a Stencil in model
def palette_apply_change
	pal_separator
	
	text = T6[:BOX_ApplyChange].sub "|", "\n"
	w, = G6.text_size(text)
	w += 10
	h = 32
	grayed_proc = proc { !@apply_change_enabled }
	tip = "#{T6[:TIP_ApplyChange]} [#{T6[:T_KEY_Enter]}]"
	hsh = { :grayed_proc => grayed_proc, :tooltip => tip, :text => text, :height => h, :width => w,
		    :bk_color => 'plum' }
	@palette.declare_button(:pal_apply_change, hsh) { apply_change }
end

#--------------------------------------------------------------
# Custom drawing for palette buttons
#--------------------------------------------------------------

#PALETTE: Drawing the Blason
def draw_button_blason(symb, dx, dy)
	lst_gl = []
	dx2 = dx/2
	dy2 = dy/2
	dym = dy * 3 / 4
	dyb = dym-4
	ptmid = Geom::Point3d.new dx2, dy2
	
	color = 'lightyellow'
	dec = 5
	lipt = [[dec,dec], [dx-dec, dec+4], [dx-dec, dy-dec], [dec, dy-dec-4]]
	pts = lipt.collect { |a| Geom::Point3d.new a.first, a.last }
	lst_gl.push [GL_POLYGON, pts, color]
	lst_gl.push [GL_LINE_LOOP, pts, 'goldenrod', 3, '']
	lst_gl.push [GL_LINE_LOOP, pts, 'khaki', 1, '']
	
	t = Geom::Transformation.scaling ptmid, 0.6, 1, 1
	radius = dx2 / 3
	pts = G6.pts_circle dx2, dy2, radius, 12
	pts = pts.collect { |pt| t * pt }
	lst_gl.push [GL_POLYGON, pts, 'darkgray']
	lst_gl.push [GL_LINE_LOOP, pts, 'gray', 1, '']

	pts = pts.each { |pt| Geom::Point3d.new(pt.x + 2, pt.y, 0) }
	lst_gl.push [GL_LINE_LOOP, pts, 'goldenrod', 1, '']
	
	lst_gl
end

#PALETTE: Custom drawing of buttons
def draw_button_opengl(symb, dx, dy, main_color, frame_color, selected)
	code = symb.to_s
	lst_gl = []
	dx2 = dx / 2
	dy2 = dy / 2
	grayed = @palette.button_is_grayed?(symb)
	color = (grayed) ? 'gray' : frame_color
	
	case code

	when /__prev/i
		pts = []
		pts.push Geom::Point3d.new(0, dy2)
		pts.push Geom::Point3d.new(dx, dy)
		pts.push Geom::Point3d.new(dx, 0)
		color = 'gray'
		lst_gl.push [GL_POLYGON, pts, color]

	when /__next/i
		pts = []
		pts.push Geom::Point3d.new(0, 0)
		pts.push Geom::Point3d.new(0, dy)
		pts.push Geom::Point3d.new(dx, dy2)
		color = 'gray'
		lst_gl.push [GL_POLYGON, pts, color]
	
	when /surfloc/
		dx4 = dx / 4
		radius = dy
		dec = 8
		yc = -dy/4
		pts1 = G6.pts_circle dx2, yc, radius, 36
		pts1 = pts1[-4..-2] + pts1[0..3]
		pts2 = G6.pts_circle dx2, yc, radius-dec, 36
		pts2 = pts2[-4..-2] + pts2[0..3]
		poly = pts1 + pts2.reverse
		dx4 = dx / 4
		dy8 = dy / 8
		lst_gl.push [GL_POLYGON, poly, 'wheat', 1, '']
		lst_gl.push [GL_LINE_LOOP, poly, 'gray', 1, '']
		pts1 = G6.pts_circle dx2, yc, radius+dec/2, 36
		pts1 = pts1[-4..-2] + pts1[0..3]
		pts2 = G6.pts_circle dx2, yc, radius-dec-dec/2, 36
		pts2 = pts2[-4..-2] + pts2[0..3]
		lines = [pts1[1], pts2[1], pts1[3], pts2[3], pts1[-2], pts2[-2]]
		lst_gl.push [GL_LINES, lines, 'red', 3, '']

	when /_flat/
		dx4 = dx / 4
		dy8 = dy / 8
		lipt = [[0,0], [dx4, dy8+2], [dx2-4, dy8+4], [dx2, dy8+6], [dx2+4, dy8+4], [dx2+dx4, dy8+2], [dx, 0]]
		pts = lipt.collect { |a| Geom::Point3d.new a.first, a.last }
		lst_gl.push [GL_LINE_STRIP, pts, 'black', 1, '']
		lipt = [[dx4, dy8+2], [dx4, dy-dy8], [dx2+dx4, dy8+2], [dx2+dx4, dy-dy8]]
		pts = lipt.collect { |a| Geom::Point3d.new a.first, a.last }
		lst_gl.push [GL_LINES, pts, 'blue', 2, '']
		lipt = [[dx4-2, dy-dy8], [dx2+dx4+2, dy-dy8]]
		pts = lipt.collect { |a| Geom::Point3d.new a.first, a.last }
		lst_gl.push [GL_LINE_STRIP, pts, 'red', 3, '']
	
	when /_plan/
		dx4 = dx / 4
		dy8 = dy / 8
		lipt = [[0,0], [dx4, dy8+2], [dx2-4, dy8+4], [dx2, dy8+6], [dx2+4, dy8+4], [dx2+dx4, dy8+2], [dx, 0]]
		pts = lipt.collect { |a| Geom::Point3d.new a.first, a.last }
		lst_gl.push [GL_LINE_STRIP, pts, 'black', 1, '']
		decx = 2
		decy = 2
		pts = G6.pts_rectangle dx2-decx, decy, 2*decx, dy-2*decy
		lst_gl.push [GL_POLYGON, pts, 'red']

	when /_single_tube/
		decl = 4
		decr = 2
		w = 6
		h = dy
		h2 = dy / 4
		color = (selected) ? 'lightgreen' : 'skyblue'
		rec1 = G6.pts_rectangle decl, 0, w, h
		rec2 = G6.pts_rectangle decl+w, 0, dx - decr - decl - 2 * w, h2
		rec3 = G6.pts_rectangle dx-decr-w, 0, w, h
		line = [rec1[0], rec1[3], rec1[2], rec2[3], rec2[2], rec3[3], rec3[2], rec3[1]]
		lst_gl.push [GL_POLYGON, rec1, color]
		lst_gl.push [GL_POLYGON, rec2, color]
		lst_gl.push [GL_POLYGON, rec3, color]
		lst_gl.push [GL_LINE_LOOP, line, 'black', 1, '']
		rec = G6.pts_rectangle decl, dy2-1, w, 4
		lst_gl.push [GL_POLYGON, rec, 'red']
		lst_gl.push [GL_LINES, rec, 'black', 2, '']
		
	when /_no_face/
		dec = 1
		h2 = dx / 3
		w = dx - dec
		color = (selected) ? 'lightgreen' : 'skyblue'
		rec1 = G6.pts_rectangle dec, 0, w, dy
		lst_gl.push [GL_POLYGON, rec1, color]
		lst_gl.push [GL_LINE_LOOP, rec1, 'black', 1, '']
		rec = G6.pts_rectangle dx2-h2/2, 0, h2, dy
		lst_gl.push [GL_LINE_LOOP, rec, 'black', 1, '-']
		rec = G6.pts_rectangle dx2-h2/2, 0, h2, 2
		lst_gl.push [GL_POLYGON, rec, 'red']
		rec = G6.pts_rectangle dx2-h2/2, dy-2, h2, 2
		lst_gl.push [GL_POLYGON, rec, 'red']
		
	when /_mark/
		dec = 2
		h2 = dx / 3
		w = dx - 2 * dec
		color = (selected) ? 'lightgreen' : 'skyblue'
		rec1 = G6.pts_rectangle dec, 0, w, dy
		lst_gl.push [GL_POLYGON, rec1, color]
		pts = G6.pts_circle dx2, dy2, dy2-1
		lst_gl.push [GL_LINE_LOOP, pts, 'blue', 1, '-']
		
	when /_punch/
		dec = 2
		h2 = dx / 3
		w = dx - 2 * dec
		color = (selected) ? 'lightgreen' : 'skyblue'
		rec1 = G6.pts_rectangle dec, 0, w, dy
		lst_gl.push [GL_POLYGON, rec1, color]
		pts = G6.pts_circle dx2, dy2, dy2-1
		lst_gl.push [GL_POLYGON, pts, 'lightgrey']
		lst_gl.push [GL_LINE_LOOP, pts, 'gray', 1, '']

	when /cut_in_group/
		pts = G6.pts_circle dx2, dy2, dy2/3
		lst_gl.push [GL_POLYGON, pts, 'red']
		lst_gl.push [GL_LINE_LOOP, pts, 'orange', 1, '']

	when /cut_material\Z/, /cur_material\Z/, /def_material\Z/
		if code =~ /cut_material\Z/
			w = 28
			x = dx - w
			color = @cut_style_color
			mat = @custom_material
		elsif code =~ /def_material\Z/
			w = dx-2
			x = 1
			color = nil
		else
			w = dx-2
			x = 1
			color = current_material_color
			mat = @matos.current
		end
		
		if color
			pts = G6.pts_rectangle x, 1, w, dy-1
			lst_gl.push [GL_POLYGON, pts, color], [GL_LINE_LOOP, pts, 'gray', 1, '']
			if mat && mat.valid? && mat.texture
				decx = w / 2
				decy = dy / 2
				pts = G6.pts_rectangle x+w-decx, 1, decx, decy
				lines = [pts[0], pts[2], pts[1], pts[3]]
				lst_gl.push [GL_POLYGON, pts, 'yellow'], [GL_LINES, lines, 'black', 1, ''], [GL_LINE_LOOP, pts, 'black', 1, '']
			end	
				
		else
			pts = G6.pts_rectangle x, 1, w, dy-1
			tri1 = pts[0..2]
			tri2 = [pts[2], pts[3], pts[0]]
			fcolor = @rendering_options["FaceFrontColor"]
			bcolor = @rendering_options["FaceBackColor"]
			lst_gl.push [GL_POLYGON, tri1, fcolor], [GL_LINE_LOOP, tri1, 'gray', 1, '']
			lst_gl.push [GL_POLYGON, tri2, bcolor], [GL_LINE_LOOP, tri2, 'gray', 1, '']
		end
	
	end	#case code
	
	lst_gl
end

#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
# VCB: Handling VCB inputs
#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
	
#VCB: always enabled	
def enableVCB? ; true ; end
	
#VCB: Input from user in the VCB	
def onUserText(text, view)
	handle_VCB(text, view)
end	
	
#VCB: Declare vcb formats
def init_VCB	
	@vcb = Traductor::VCB.new
	@vcb.declare_input_format :offset, "l"
end

#VCB: Handle VCB input
def handle_VCB(text, view)
	return UI.beep unless @vcb
	nb_errors = @vcb.process_parsing(text)
	
	if nb_errors > 0
		lmsg = []
		@vcb.each_error { |symb, s| lmsg.push "<#{s}>" }
		msg = "#{T6[:T_ERROR_InVCB]} \"#{text}\"  --> #{lmsg.join(' - ')}"
		@palette.set_message msg, 'E'
		view.invalidate
	else
		action_from_VCB(text)
	end	
end

#VCB: Execute actions from the VCB inputs for the grid dimensions	
def action_from_VCB(text)
	offset = nil
	@vcb.each_result do |symb, val, suffix|
		case symb
		when :offset
			offset = val
		end	
	end
	
	if offset
		if [:emboss, :carve].include?(@cut_style)
			if offset < 0
				if @cut_style == :emboss
					@cut_style = :carve
				elsif @cut_style == :carve
					@cut_style = :emboss
				end
			end	
		end	
		cut_style_modify_carve_offset offset.abs
	end	
end

end	#class VisuHoleTool

end	#End Module F6_VisuHole
