=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Copyright 2012 - Designed April 2012 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			:   CurvizardTool.rb
# Original Date	:   21 Apr 2012 - version 1.0
# Description	:   Manage the interactive GUI tool for Curvizard
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_Curvizard

T6[:TIT_MakeCurve] = "Make Curves"

T6[:TIP_ExecAlgo] = "Validate contours and go to preview mode"
T6[:TIP_NextCurve] = "Validate contours and ready for to enter a next contour"
T6[:TIP_ExecGeometry] = "Finish and Generate geometry"
T6[:TIP_Rollback] = "Undo and Go back to Contour selection"
T6[:BOX_CurveOperations] = "Tools"
T6[:TIP_CurveOperations] = "Tool to be applied to curves and sequence of edges"	
T6[:MSG_RubyError] = "The operation is aborted"	
T6[:MSG_PluginLoadError] = "The plugin %1 could not be loaded"	

#--------------------------------------------------------------------------------------------------------------
# Application Launcher: Call back called by LibFredo6 for executing menus
#--------------------------------------------------------------------------------------------------------------			 				   
	
#Actual launching of menu commands	
def F6_Curvizard.action__mapping(action_code, from)
	F6_Curvizard.launch_tool({ :plugin => action_code, :from => from })
end

def F6_Curvizard.launch_tool(*args)
	@tool = CVZ_Tool.new *args
	Sketchup.active_model.select_tool @tool
end

def F6_Curvizard.persistence_set(symb, val)
	@hsh_persistence = {} unless @hsh_persistence
	@hsh_persistence[symb] = val
end

def F6_Curvizard.persistence_get(symb)
	return nil unless @hsh_persistence
	@hsh_persistence[symb]
end

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CVZ_Tool : SU Tool Class to manage GUI for Loft functions
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
					
class CVZ_Tool < Traductor::PaletteSuperTool

XCurveInfo = Struct.new :valid, :hcurves, :parent, :tr, :llines, :computed

@@lst_options_global = [[:edgepicker_modifier, 'F'], [:edgepicker_anglemax, 40], [:edgepicker_stop_at_crossing, true],
                        [:edge_prop_filter, 'P']]

#CVZ_Tool: Class instance initialization
def initialize(*args)
	#Loading parameters
	options_load_global
		
	#Getting the list of plugins and information
	init_all_plugins
	
	#Init environment
	@mode = :selection
	results_clear
	@view_tracker = G6::ViewTracker.new
	
	#Parsing the arguments
	args.each { |arg| arg.each { |key, value|  parse_args(key, value) } if arg.class == Hash }

	#Resetting environment
	@hilight_duration = 60.0
	@exit_duration = 0.5
	@auto_exit = MYDEFPARAM[:DEFAULT_Auto_Exit]
	
	#Loading strings
	init_text
	init_colors
	
	#Initializing the cursors and texts
	init_cursors
	
	#Initializating the Edge Picker
	init_edge_picker
	
	#Creating the palette manager and texts
	init_palette
	palette_update_visible
	
	#Visual Panels
	hsh = { :delay => -1, :mini_delay => -1 }
	@vpanel_abort = Traductor::VisualPanel.new T6[:T_STR_AbortTool], hsh
end

#CVZ_Tool: Initialize the plugin environment
def init_all_plugins
	@hsh_plugin_options = {}
	@lst_plugins = F6_Curvizard.plugin_list
	@hsh_plugin_info = F6_Curvizard.plugin_info
	@hsh_plugin_algo = {}
	@hsh_params = {}
	plugin_notify_proc = self.method "notify_from_plugin"

	@lst_plugins.each do |plugin|
		#Loading options from registry
		options_load_plugin plugin
		hsh_options = @hsh_plugin_options[plugin]
		hsh_options = {} unless hsh_options
		
		#Creating the Algo class
		begin
			@hsh_plugin_algo[plugin] = eval("Algo_#{plugin}.new plugin, hsh_options, plugin_notify_proc")
		rescue Exception => e
			@suops.abort_operation
			Traductor::RubyErrorDialog.invoke e, plugin, T6[:MSG_PluginLoadError, plugin]		
			return exit_tool
		end
	end	
	
	@lst_plugins.each do |plugin|
		algo = @hsh_plugin_algo[plugin]
	end	
end

#CVZ_Tool: Initialize colors
def init_colors
	@xcurve_colors = []
	colors = ['green', 'yellow', 'blue', 'purple', 'red', 'magenta', 'brown', 'orange', 'darkgreen', 'limegreen'] 
	@xcurve_colors = colors.collect { |color| Sketchup::Color.new color }
	@xcurve_colors.each { |color| color.alpha = 0.3 }
end

#CVZ_Tool: Initialize cursors
def init_cursors
	hotx = 5
	hoty = 0
	@id_cursor_default = Traductor.create_cursor "Cursor_Arrow_Default", 2, 2
	@id_cursor_arrow_exit = Traductor.create_cursor "Cursor_Arrow_Exit", 0, 0	
	@id_cursor_validate = Traductor.create_cursor "Cursor_Validate", 0, 0	
	@id_cursor_hourglass_red = Traductor.create_cursor "Cursor_hourGlass_Red", 16, 16		
end

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

#CVZ_Tool: Initialization of texts
def init_text
	@mnu_exit = T6[:T_BUTTON_Exit]	
	@tip_rollback = T6[:TIP_Rollback]
	@tip_next_curve = T6[:TIP_NextCurve]
	@tip_exec_geometry = T6[:TIP_ExecGeometry]
	@txt_seg = T6[:T_TXT_Segments]
end

#CVZ_Tool: Initializating the Edge Selection Picker
def init_edge_picker
	hsh = {}
	hsh[:notify_proc] = self.method 'notify_edge_picked'
	hsh[:mouseover_proc] = self.method 'notify_mouseover'
	hsh[:modifier] = @hsh_options_global[:edgepicker_modifier]
	hsh[:anglemax] = @hsh_options_global[:edgepicker_anglemax]
	hsh[:stop_at_crossing] = @hsh_options_global[:edgepicker_stop_at_crossing]
	hsh[:edge_prop_filter] = @hsh_options_global[:edgepicker_edge_prop_filter]
	hsh[:title] = @title
	hsh[:no_vcb] = true
	hsh[:vbox_option_exit] = true
	@selmode = Traductor::EdgePicker.new hsh
end

#---------------------------------------------------------------------------------------------
# OPTIONS: Option Management and storage
#---------------------------------------------------------------------------------------------
				
#OPTIONS: Load options from registry
def options_load_global
	#Global GUI parameters for Curvizard
	sparam = Sketchup.read_default "Curvizard", "Options"
	hsh = {}
	if sparam
		hsh = eval(sparam) rescue nil
	end
	@@lst_options_global.each do |a|
		key, default = a
		hsh[key] = default unless hsh.has_key?(key)
	end
	@hsh_options_global = hsh
end

#OPTIONS: Load options for a plugin
def options_load_plugin(plugin)
	sparam = Sketchup.read_default "Curvizard", plugin.to_s
	hsh = {}
	if sparam
		hsh = eval(sparam) rescue nil
	end
	@hsh_plugin_options[plugin] = hsh
end

#OPTIONS: Save options to registry
def options_save
	#Getting the edgepicker parameters
	hsh = @selmode.get_hparams
	@hsh_options_global[:edgepicker_modifier] = hsh[:modifier]
	@hsh_options_global[:edgepicker_anglemax] = hsh[:anglemax]
	@hsh_options_global[:edgepicker_stop_at_crossing] = hsh[:stop_at_crossing]
	@hsh_options_global[:edgepicker_edge_prop_filter] = hsh[:edge_prop_filter]
	
	#Saving global options
	sparam = @hsh_options_global.inspect.gsub('"', "'")
	Sketchup.write_default "Curvizard", "Options", sparam	
	
	#Plugin options
	@lst_plugins.each do |plugin|
		algo = @hsh_plugin_algo[plugin]
		sparam = algo.get_hoptions.inspect.gsub('"', "'")
		Sketchup.write_default "Curvizard", plugin.to_s, sparam	
	end
end
		
#--------------------------------------------------
# Activation / Deactivation
#--------------------------------------------------

#Tool activation
def activate
	LibFredo6.register_ruby "Curvizard::#{@title}" if defined?(LibFredo6.register_ruby)
	Traductor::Hilitor.stop_all_active
	@model = Sketchup.active_model
	@selection = @model.selection
	@entities = @model.active_entities
	@view = @model.active_view
		
	#Resetting the environment
	reset_context
	
	#Initiating the palette
	set_state_mode :selection

	#Initializing the Execution environment
	hsh = { :title => @title_ops }
	@suops = Traductor::SUOperation.new hsh

	#Handling the initial selection
	handle_initial_selection if @selection.length > 0
		
	@view.invalidate
	show_message
end

#Manage the initial selection or the previous results
def handle_initial_selection
	set_state_mode :computing
	t = F6_Curvizard.persistence_get :results_time
	if t && (Time.now - t) < 1.0
		@made_vertices = F6_Curvizard.persistence_get :results
		results_reselect
		execute_validate
	else
		auto_extend_selection
		@old_selection = @selection.to_a.clone
		@selmode.check_initial_selection
		lst_info_contours = @selmode.get_info_contours
		if lst_info_contours.length == 0
			set_state_mode :selection
			return false
		end	
		@initial_selection_ok = true
		top_execute
	end
end

#Reset context variables
def reset_context
	@button_down = false
	@hsh_xcurve_edges = {}
	@hsh_xcurve_parent = {}
	rollback_clear
	results_clear
end

#Tool Deactivation
def deactivate(view)
	@suops.finish_operation
	hilight_shut_down
	@algo.deactivating if @algo
	options_save
	results_persist
	selection_at_exit
	view.invalidate
end
	
#Set / reset the selection at exit
def selection_at_exit
	sel = []
	if @made_vertices && !@made_vertices.empty?
		@made_vertices.each do |a| 
			lvx, parent = a
			next unless lvx && parent == Sketchup.active_model && !(lvx.find { |vx| ! vx.valid? })
			sel += G6.edges_from_vertices_in_sequence(lvx)
		end	
	else	
		sel = @old_selection.find_all { |a| a.valid? && a.instance_of?(Sketchup::Edge) } if @old_selection
	end
	@selection.add sel unless sel.empty?
end
	
#Auto extend the edge selection based on continuity	
def auto_extend_selection
	return unless @plugin == :make_curve
	return if @selection.length == 0
	
	ledges = @selection.grep(Sketchup::Edge)
	return if ledges.empty?

	hsh_edges = {}
	ledges.each do |edge|
		id = edge.entityID
		next if hsh_edges[id]
		ledges = edge_extend_continuous(edge)
		ledges.each { |e| hsh_edges[e.entityID] = e }
	end
	
	@selection.add hsh_edges.values
end	

#Extend an edge in continuity
def edge_extend_continuous(edge)
	a = @selmode.get_hparams[:anglemax]
	angle_continuity = (a) ? (180 - a).degrees : 140.degrees
	ll = edge_extend_one_side(edge.start, edge, angle_continuity)
	lr = edge_extend_one_side(edge.end, edge, angle_continuity)
	ledges = ll.reverse + lr[1..-1]
end

#Finding the path from the vertex
def edge_extend_one_side(vx0, edge0, angle_continuity)
	ledges = []
	edge_ori = edge0
	hsh = {}
	
	while edge0
		break if hsh[edge0.entityID]
		ledges.push edge0
		hsh[edge0.entityID] = true
		curve0 = edge0.curve
		vx_next = edge0.other_vertex vx0
		angle_max = angle_continuity
		next_edge = nil
		next_edges = vx_next.edges
		break if next_edges.length > 2 && (next_edges.find_all { |e| !e.soft? }).length > 2
		next_edges.each do |e|
			next if e == edge0 #|| e.faces.length > 0
			if (next_edges.length == 2 && e.faces.length == 0) || (curve0 && e.curve == curve0)
				next_edge = e
				break
			end	
			angle = G6.edges_angle_at_vertex(edge0, e, vx_next)
			if angle > angle_max
				angle_max = angle
				next_edge = e
			end	
		end
		
		#End of the path
		break unless next_edge
		break if next_edge == edge_ori
		
		#Going forward
		edge0 = next_edge
		vx0 = vx_next
	end
	
	ledges
end

#--------------------------------------------------------------------------------	
#--------------------------------------------------------------------------------	
# Plugin Management
#--------------------------------------------------------------------------------	
#--------------------------------------------------------------------------------	

#PLUGIN: Change the current plugin
def plugin_change(plugin)
	return if plugin == @plugin
	
	#Deactivating the current plugin
	@algo.deactivating if @algo
	
	#Getting info on plugin
	return unless plugin
	plugin_info = F6_Curvizard.plugin_info[plugin]
	return unless plugin_info
	
	#Getting the Algorithm class
	algo_new = @hsh_plugin_algo[plugin]
	return unless algo_new
	@algo = algo_new
	
	#Setting the current plugin
	@plugin = plugin
	@plugin_name = "Curvizard##{plugin}"
	@title = T6[plugin_info[:symb_mnu]]
	
	#Setting the current parameters
	@hsh_params = @algo.get_hparams
	@hi_display = value_param :hi_display, nil
	@hi_label = value_param :hi_label, nil
	@hi_color_scheme = value_param :hi_color_scheme, 'black'
	@hi_color_mark = value_param :hi_color_mark, 'black'
	@hi_label_bk_color = value_param :hi_label_bk_color, 'lightyellow'
	@hi_label_fr_color = value_param :hi_label_fr_color, 'gold'
	@hi_stipple = value_param :hi_stipple, ''
	@hi_width = value_param :hi_width, 3
	
	@selmode.set_title @title if @selmode
	@tip_validate = @title
	@title_ops = "Curvizard - " + @title
	
	#Updating the palette
	palette_update_visible

	#Executing activating procedure if any
	@algo.activating
	
	#Reselecting the result edges if applicable
	results_reselect
	show_message
	if @view
		@force_refresh = true
		@view.invalidate
	end	
end

#Return the value of a parameter
def value_param(symb, default)
	hsh = @algo.get_hparams
	hsh.has_key?(symb) ? hsh[symb] : default
end

#PLUGIN: Notification procedure from plugin
def notify_from_plugin(action, *args)
	case action
	when :set_option
		retry_execute
	when :delete_edges	
		deletion_erase_edges *args
	end	
	show_message
end

#--------------------------------------------------
# Callback methods from palette
#--------------------------------------------------

#Notification method from palette for Back and Exec buttons
def notify_from_palette(action, code)
	case action
	when :tip
		compute_tooltip_for_palette code
	when :gray	
		compute_gray_for_palette code
	when :exec
		execute_from_palette code
	end	
end

#Tooltip for Back and Execute buttons
def compute_tooltip_for_palette(code)
	case code
	when :validate
		@tip_validate	
	when :back
		@tip_rollback
	when :next_curve
		@tip_next_curve
	end
end

#Gray status for Back and Execute buttons
def compute_gray_for_palette(code)
	case code
	when :validate
		!validate_allowed?		
	when :back
		!rollback_allowed?
	when :next_curve
		!validate_current_allowed?
	end	
end

#Execution for Back and Execute buttons
def execute_from_palette(code)
	case code
	when :validate
		execute_validate
	when :back
		rollback_execute
	when :next_curve
		validate_current_execute
	end	
end

#Check if current contour can be validated
def validate_allowed?
	!(@selmode.empty?)
end

#Check if current contour can be validated
def validate_current_allowed?
	!@selmode.contours_validated?
end

#Validation of current curve and go to next
def validate_current_execute
	@selmode.validate_current_selection if validate_current_allowed?
end

#Validate action, depending on the current mode
def execute_validate
	case @mode
	when :selection
		@selmode.terminate_current_selection
	end	
end

#---------------------------------------------------------------------------------------
# Undo Management
#---------------------------------------------------------------------------------------

#UNDO: Cancel and undo methods
def onCancel(flag, view)
	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
	if @rollback_info.length > 0
		UI.start_timer(0) { rollback_execute true }
	elsif @mode == :selection	
		@suops.start_operation_fake
		UI.start_timer(0) { @suops.finish_operation }
		@selmode.handle_escape
	end	
end

#UNDO: Handle the escape key depending on current mode
def handle_escape
	if @rollback_info.length > 0
		rollback_execute
	elsif @mode == :selection
		@selmode.handle_escape
	end	
end

#----------------------------------------------------------------
# VCB Methods
#----------------------------------------------------------------

#VCB Inputs
def enableVCB? ; @algo.get_VCB ; end
def onUserText(text, view)
	vcb = @algo.get_VCB
	return UI.beep unless vcb
	nb_errors = vcb.process_parsing(text)
	
	#Errors in VCB inputs
	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
		return
	end

	#Parsing the results
	@algo.parse_VCB vcb
end

#----------------------------------------------------------------
# Cursor Management
#----------------------------------------------------------------

#Setting the cursor
def onSetCursor
	ic = @suops.onSetCursor
	return ic if ic
	
	ic = super
	return (ic != 0) if ic
	
	cursor = 0
	case @mode
	when :selection
		cursor = @selmode.onSetCursor
	when :computing
		cursor = @id_cursor_hourglass_red
	end
	UI::set_cursor cursor
end

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

#Contextual menu
def getMenu(menu)
	cxmenu = Traductor::ContextMenu.new
	
	#Edgepicker menu contribution
	if @mode == :selection
		@selmode.contextual_menu_contribution(cxmenu)
	end
	
	#Other menus for options
	
	#Menu Validate and Rollback
	cxmenu.add_sepa
	cxmenu.add_item(@tip_rollback) { rollback_execute } if rollback_allowed?
	cxmenu.add_item(@tip_next_curve) { validate_current_execute } if validate_current_allowed?
	cxmenu.add_item(@tip_exec_geometry) { execute_validate } if validate_allowed?

	#Other plugin
	cxmenu.add_sepa
	F6_Curvizard.plugin_list.each do |plugin|
		hinfo = F6_Curvizard.plugin_info[plugin]
	end
	
	#Exit
	cxmenu.add_sepa
	cxmenu.add_item(@mnu_exit) { exit_tool }
	
	#Showing the menu
	cxmenu.show menu
	true
end

#Return key pressed
def onReturn(view)
	execute_validate
end

#Exit the tool
def exit_tool
	@model.select_tool nil 
end

#Abort the operation and exit
def abort_tool
	rollback_execute
	exit_tool
end

#----------------------------------------------------------------
# View Management
#----------------------------------------------------------------

#Resume after view tools and recalculation
def resume(view)
	onMouseMove_zero
end	

def suspend(view)
	
end

#----------------------------------------------------------------
# Button click methods
#----------------------------------------------------------------

#Button Down
def onLButtonDown(flags, x, y, view)
	#Palette management
	return if super
	
	#Dispatching the event
	case @mode
	when :selection
		if @selmode.onLButtonDown(flags, x, y, view)
			rollback_clear
			results_clear
		end	
	when :computing
		exit_tool
	end	
	onMouseMove_zero
end

#Button Up
def onLButtonUp(flags, x, y, view)
	return if super
	case @mode
	when :selection	
		@selmode.onLButtonUp(flags, x, y, view)
	end	
	@button_down = false
	onMouseMove_zero
end

#Double Click received
def onLButtonDoubleClick(flags, x, y, view)
	return if super
	case @mode
	when :selection	
		@selmode.onLButtonDoubleClick(flags, x, y, view)
	end	
	view.invalidate
end

#----------------------------------------------------------------
# Keyboard Management
#----------------------------------------------------------------

#Key Up
def onKeyUp(key, rpt, flags, view)
	key = Traductor.check_key key, flags, true
	
	#Dispatching the event
	case @mode
	when :selection
		return onMouseMove_zero if @selmode.onKeyUp(key, rpt, flags, view)
	end
		
	@control_down = false
end

#Key down
def onKeyDown(key, rpt, flags, view)
	key = Traductor.check_key key, flags, false

	#Dispatching the event
	case @mode
	when :selection
		return view.invalidate if @selmode.onKeyDown(key, rpt, flags, view)
	end
	
	case key			
	#Calling options
	when CONSTRAIN_MODIFIER_KEY
		onMouseMove_zero

	when COPY_MODIFIER_KEY
		@control_down = true
		return

	else
		@control_down = false
		return
	end	
	@control_down = false
	
	onMouseMove_zero
	show_message
end

#----------------------------------------------------------------
# Mouse move Methods
#----------------------------------------------------------------

#Mouse Move method
def onMouseLeave(view)
	@mouseOut = true
	show_message
	view.invalidate
end

def onMouseEnter(view)
	@mouseOut = false
	view.invalidate
end

def onMouseMove_zero ; 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
		view.invalidate
		show_message
		return
	end	
	@mouse_in_palette = false
	

	#Synchronize draw and move
	return if @moving
	@moving = true

	#Dispatching the event
	case @mode
	when :selection	
		@selmode.onMouseMove(flags, x, y, view)
	end
	
	#Refreshing the view
	view.invalidate
	show_message
end	

#----------------------------------------------------------------
# Drawing Preparation and calculation
#----------------------------------------------------------------

def dessin_compute
	dessin_compute_highlights
	dessin_compute_original_curves
	@timer_dessin = nil
end

#DESSIN: Compute the original contours
def dessin_compute_original_curves
	@dessin_original_lines = []
	@dessin_original_crosses = []
	return unless @hi_display && @hilight_on && !@hilight_info.empty?
	
	@original_contours.each do |pts|
		@dessin_original_lines.push pts
		pts2d = @view.screen_coords pts[0]
		@dessin_original_crosses += G6.pts_cross(pts2d.x, pts2d.y, 3)
	end
end

#DESSIN: Compute the highlighted contours
def dessin_compute_highlights
	@dessin_hi_lines = []
	return unless @hi_display && @hilight_on && !@hilight_info.empty?
	ncolors = @hi_color_scheme.length
	for i in 0..ncolors-1
		@dessin_hi_lines[i] = []
	end	
	colors = @hi_color_scheme
	color_mark = @hi_color_mark
	stipple = @hi_stipple
	
	for i in 0..@hilight_info.length-1
		j = i.modulo(ncolors)
		normal, pts = @hilight_info[i]
		@dessin_hi_lines[j].push pts.collect { |pt| G6.small_offset @view, pt }
	end	
end

#----------------------------------------------------------------
# Drawing Methods
#----------------------------------------------------------------

#Draw method for Polyline tool
def draw(view)	
	#Check if changed
	if @view_tracker.changed?
		if @timer_dessin
			UI.stop_timer @timer_dessin
		end	
		@timer_dessin = UI.start_timer(0.1) { dessin_compute ; view.invalidate }
		super
		return
	end	
	
	#Drawing the curves
	draw_hilights view
	draw_original_curves view
	xcurve_draw view
	case @mode
	when :selection
		@selmode.draw view unless @mode == :computing #unless !@force_refresh && (@mouseOut || @mouse_in_palette)
	end	
	@moving = false
	@force_refresh = false
	
	#Drawing the palette
	super
end

#Draw the original contours
def olddraw_original_curves(view)
	return unless @hi_display && @hilight_on && !@hilight_info.empty?
	return if !@dessin_original_lines || @dessin_original_lines.empty?
	view.line_stipple = ''
	view.line_width = 1
	
	view.drawing_color = 'green'
	@dessin_original_lines.each do |pts| 
		view.draw GL_LINE_STRIP, pts
	end	
	
	view.drawing_color = 'red'
	view.draw2d GL_LINES, @dessin_original_crosses
end

def draw_original_curves(view)
	return unless @hi_display && @hilight_on && !@hilight_info.empty?
	color = 'green'
	width = 1
	stipple = ""
	view.line_stipple = stipple
	view.line_width = width
	view.drawing_color = color
	
	lpt_cross = []
	lpt_cross_beg = []
	@original_contours.each do |pts|
		view.draw GL_LINE_STRIP, pts
		pts2d = pts.collect { |pt| view.screen_coords pt }
		lpt = (pts2d.first == pts2d.last) ? pts2d[1..-2] : pts2d[1..-1]
		lpt.each { |pt| lpt_cross += G6.pts_cross(pt.x, pt.y, 3) }
		pt = pts2d[0]
		lpt_cross_beg += G6.pts_cross(pt.x, pt.y, 3)
	end
	#view.draw2d GL_LINES, lpt_cross
	view.drawing_color = 'red'
	view.draw2d GL_LINES, lpt_cross_beg
end

#Draw the hilighted edges
def olddraw_hilights(view)
	return unless @hi_display && @hilight_on && !@hilight_info.empty?
	return if !@dessin_hi_lines || @dessin_hi_lines.empty?
	colors = @hi_color_scheme
	color_mark = @hi_color_mark
	ncolors = colors.length
	
	#Drawing the highlighted curve
	view.line_stipple = @hi_stipple
	view.line_width = @hi_width
	@dessin_hi_lines.each_with_index do |llines, i|
		view.drawing_color = colors[i] 
		llines.each do |line| 
			view.draw GL_LINE_STRIP, line 
			draw_hi_dots view, line, color_mark
		end	
	end	
	
	#Drawing the arrows or the dots
	
	#Drawing the labels
end

def draw_hilights(view)
	return unless @hi_display && @hilight_on && !@hilight_info.empty?
	return if !@dessin_hi_lines || @dessin_hi_lines.empty?
	colors = @hi_color_scheme
	color_mark = @hi_color_mark
	stipple = @hi_stipple
	ncolors = colors.length
	
	#Drawing the highlighted curve
	view.line_stipple = @hi_stipple
	view.line_width = @hi_width
	@dessin_hi_lines.each_with_index do |llines, i|
		view.drawing_color = colors[i] 
		llines.each do |line| 
			view.draw GL_LINE_STRIP, line 
		end	
	end	

	for i in 0..@hilight_info.length-1
		color = colors[i.modulo(ncolors)]
		normal, pts = @hilight_info[i]
		case @hi_display
		when :arrow
			draw_begin_arrow view, normal, pts, color
		when :dot
			draw_hi_dots view, pts, color_mark
		end	
		draw_hi_label view, pts
	end	
end

#Draw the highlight label
def draw_hi_label(view, pts)
	case @hi_label
	when :nb_seg
		text = "#{pts.length - 1} #{@txt_seg}"
	else
		return
	end
	
	#n = pts.length / 3
	ptmid = G6.curve_barycenter pts
	pt2d = view.screen_coords ptmid
	x = pt2d.x
	y = pt2d.y + 50
	
	G6.draw_rectangle_text view, x, y, text, @hi_label_bk_color, @hi_label_fr_color, 0, 2
end

#Draw the hilighted edges
def draw_hi_dots(view, pts, color)
	return unless pts && pts.length > 0
	view.drawing_color = (color) ? color : 'black'
	lquads = []
	pts.each do |pt|
		pt2d = view.screen_coords pt
		lquads += G6.pts_square(pt2d.x, pt2d.y, 2)
	end	
	view.draw2d GL_QUADS, lquads
end

#Draw the hilighted edges
def draw_hi_lines(view, pts, color, width=3, stipple='')
	return unless pts && pts.length > 1
	
	#Drawing the line
	pts = pts.collect { |pt| G6.small_offset view, pt }
	view.line_width = (width) ? width : 3
	view.line_stipple = (stipple) ? stipple : ''
	view.drawing_color = (color) ? color : 'black'
	view.draw GL_LINE_STRIP, pts
end

#Draw the hilighted edges
def draw_begin_arrow(view, normal, lines, color=nil)
	pt1, pt2 = lines
	vec12 = pt1.vector_to pt2
	vecp = vec12 * normal
	return unless vecp.valid?
	d = view.pixels_to_model 8, pt1
	pa0 = pt1.offset vecp, d
	pa1 = pt1.offset vecp, -d
	d2 = [vec12.length, 2 * d].min
	pa2 = pt1.offset vec12, d2
	pts = [pa0, pa1, pa2].collect { |pt| G6.small_offset view, pt }
	
	view.drawing_color = (color) ? color : 'black'
	view.draw GL_POLYGON, pts
	view.line_width = 1
	view.line_stipple = ''
	view.drawing_color = 'black'
	view.draw GL_LINE_LOOP, pts	
end

#----------------------------------------------------------------
# Results Management
#----------------------------------------------------------------

#RESULTSClears the result information
def results_clear
	@made_vertices = []
	@hilight_info = []
	@result_computed = false
end

#RESULTS: check if results were computed
def results_previous?
	@result_computed
	#@made_vertices.length > 0
end

#RESULTS: Register the highlight information
def results_persist
	if @made_vertices.empty?
		F6_Curvizard.persistence_set :results, nil
		F6_Curvizard.persistence_set :results_time, nil
	else	
		F6_Curvizard.persistence_set :results, @made_vertices
		F6_Curvizard.persistence_set :results_time, Time.now
	end	
end

#RESULTS: Register the execution of operation (when no new vertices created)
def results_register_computed
	@result_computed = true
end

#RESULTS: Register the highlight information
def results_register_vertices(lvx, parent, tr)
	lvx = lvx.find_all { |vx| vx.valid? }
	@made_vertices.push [lvx, parent, tr]
	@hilight_info.push vertices_info(lvx, tr) 
	edge = lvx[0].common_edge lvx[1]
	xcurve_reset_all	
	#xcurve_reset edge, parent, tr	
	@result_computed = true
end

#RESULTS: Restore the Results as the selection in the Edge Picker
def results_reselect
	return if @made_vertices.empty?
	@selmode.reset
	@hilight_info = []
	@made_vertices.each do |info|
		lvx, parent, tr = info
		ledges = G6.edges_from_vertices_in_sequence lvx
		@selmode.register_edges ledges, tr, parent
	end	
end

#RESULTS: Calculate the results information in top level 3D (vertices and normal to first edge)
def vertices_info(lvx, tr)	
	pts = lvx.collect { |vx| tr * vx.position }
	return if pts.length < 2
	pt0 = pts[0]
	vec0 = pt0.vector_to pts[1]
	
	#Finding the plane where to draw the arrow
	normal = nil
	for i in 1..pts.length-2
		vec1 = pts[i].vector_to pts[i+1]
		normal = vec0 * vec1
		break if normal.valid?
	end	
	unless normal
		edge0 = lvx[0].common_edge lvx[1]
		if edge0
			face = edge0.faces[0]
			normal = face.normal if face
		end
	end
	normal = vec0.axes[0] unless normal
	
	[normal, pts]
end

#----------------------------------------------------------------
# Message Display
#----------------------------------------------------------------

def refresh_after_operation
	@view.invalidate
	show_message
end

#display information in the Sketchup status bar
def show_message
	if @mouseOut
		@selmode.set_tooltip nil
		palette_set_message nil
	elsif @mouse_in_palette
		@selmode.set_tooltip nil
		palette_set_message @palette.get_main_tooltip, 'aliceblue'
	end	

	@selmode.info_show if @selmode && @mode == :selection
	
	@algo.show_VCB
end

#-----------------------------------------------------------------
# Interface to Algo for Execution / Termination
#-----------------------------------------------------------------

#Set the current mode
def set_state_mode(mode)
	@mode = mode
	case @mode
	when :selection
		visi = @mode
	else
		visi = nil
	end
	@view.invalidate
end

#Stop the highlighting after the computation
def hilight_shut_down
	return unless @timer_highlight
	@hilight_on = false
	UI.stop_timer @timer_highlight
	@timer_highlight = nil
	(@quick_exit) ? exit_tool : onMouseMove_zero
end

#-----------------------------------------------------------------------------------------
# DELETION: Edge Deletion
#-----------------------------------------------------------------------------------------

#DELETION: Register edges for deletion
def deletion_xcurve_edges(ledges, parent)
	lp = @hsh_xcurve_parent[parent.entityID]
	return unless lp
	ledges.each do |edge|
		key = xcurve_key_edge(edge, parent)
		curvinfo = @hsh_xcurve_edges[key]
		@hsh_xcurve_edges.delete key
		lp.delete curvinfo
	end	
end

#DELETION: Perform the deletion of edges
def deletion_erase_edges(lst_parent_edges)
	lst_parent_edges.each do |a|
		ledges, parent = a
		ledges = ledges.find_all { |e| e.valid? }
		next if ledges.empty?
		deletion_xcurve_edges ledges, parent
		entities = G6.grouponent_entities(parent)
		entities.erase_entities ledges
	end	
end
	
#-----------------------------------------------------------------------------------------
# XCURVE: Handling display of existing SU curves
#-----------------------------------------------------------------------------------------

#XCURVE: Notification from the EdgePicker on mouse over edge or face
def notify_mouseover(code, entity, parent, tr)
	entity = entity.edges[0] if entity.instance_of?(Sketchup::Face)
	@parent = parent
	key = xcurve_key_edge(entity, parent)
	return if @hsh_xcurve_edges[key]
	xcurve_explore entity, parent, tr
end

#XCURVE: Reset variables for management of existing curves
def xcurve_reset(edge, parent, tr)
	key = xcurve_key_edge(edge, parent)
	curvinfo = @hsh_xcurve_edges[key]
	@hsh_xcurve_edges.delete key	
	@hsh_xcurve_parent[parent.entityID] = nil
	xcurve_explore(edge, parent, tr)
end

#XCURVE: Reset all variables for Xcurve management
def xcurve_reset_all
	@hsh_xcurve_edges = {}
	@hsh_xcurve_parent = {}
end
	
#XCURVE: Compute the key for an edge
def xcurve_key_edge(edge, parent)
	"#{edge.entityID} - #{parent.entityID}"
end
	
#XCURVE: Explore the adjacent existing curves
def xcurve_explore(entity, parent, tr)
	return unless entity.valid?
	ledges = entity.all_connected.find_all { |e| e.instance_of?(Sketchup::Edge) }
	hcurves = {}
	ledges.each do |e|
		curve = e.curve
		hcurves[curve.entityID] = curve if curve && !hcurves[curve.entityID]
	end	
	return if hcurves.empty?
	
	curvinfo = xcurve_info_create(hcurves, parent, tr)
	ledges.each { |e| @hsh_xcurve_edges[xcurve_key_edge(e, parent)] = curvinfo if e.valid? }
	
	lp = @hsh_xcurve_parent[parent.entityID] 
	lp = @hsh_xcurve_parent[parent.entityID] = [] unless lp
	lp.push curvinfo
end

#XCURVE: Explore the adjacent existing curves
def xcurve_info_create(hcurves, parent, tr)
	curvinfo = XCurveInfo.new
	curvinfo.valid = false
	curvinfo.hcurves = hcurves
	curvinfo.parent = parent
	curvinfo.tr = tr
	curvinfo
end

#XCURVE: Compute the display lines for the curvinfo
def xcurve_compute(curvinfo)
	return if curvinfo.computed
	tr = curvinfo.tr
	llines = []
	curvinfo.hcurves.each do |k, curve|
		next unless curve.valid?
		pts = curve.vertices.collect { |v| tr * v.position }
		llines.push pts
	end
	curvinfo.llines = llines
	curvinfo.computed = true
	curvinfo
end

#XCURVE: Draw the Xcurves for the current parent
def xcurve_draw(view)
	return unless @parent && @parent.valid?
	hshp = @hsh_xcurve_parent[@parent.entityID]
	return unless hshp && hshp.length > 0
	
	view.line_width = 8
	view.line_stipple = ''
	nc = @xcurve_colors.length
	hshp.each do |curvinfo|
		xcurve_compute curvinfo
		curvinfo.llines.each_with_index do |pts, i| 
			view.drawing_color = @xcurve_colors[i.modulo(nc)]
			view.draw GL_LINE_STRIP, pts
		end	
	end
end

#--------------------------------------------------------------
#--------------------------------------------------------------
# Palette Management
#--------------------------------------------------------------
#--------------------------------------------------------------

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

#PALETTE: Update the visibility status of buttons
def palette_update_visible
	return unless @palette
	@palette.visible_family @mode, @plugin
end

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

	#Buttons for Plugins
	pal_separator
	symb_master = :pal_all_plugins
	bk_color = 'lightblue'
	hsh = { :type => 'multi_free', :passive => true, :draw_proc => @draw_local, :bk_color => bk_color,
	        :height => 8, :tooltip => T6[:TIP_CurveOperations] }
	@palette.declare_button(symb_master, hsh)
	hshp = { :parent => symb_master, :height => 24, :width => 24, :hi_color => 'lightgreen', :bk_color => bk_color }	
	@lst_plugins.each { |plugin| palette_button_plugin plugin, hshp }
	
	#Modifiers for Selection mode
	pal_separator
	@palette.set_current_family :selection
	@selmode.contribute_palette @palette, 'MHE' if @selmode

	#Abort and Exit
	pal_separator
	@palette.set_current_family 
	grayed_proc = proc { !rollback_allowed? }
	hsh = { :grayed_proc => grayed_proc, :draw_proc => :std_abortexit, :main_color => 'red', :frame_color => 'green', 
	        :tooltip => T6[:T_STR_AbortTool] }
	@palette.declare_button(:pal_abort, hsh) { abort_tool }

	#Exit tool
	@palette.set_current_family 
	hsh = { :draw_proc => :std_exit, :tooltip => T6[:T_STR_ExitTool] }
	@palette.declare_button(:pal_exit, hsh) { exit_tool }
		
	#Execute
	ssb = :back
	tip_proc = proc { notify_proc.call :tip, ssb }
	grayed_proc = proc { notify_proc.call :gray, ssb }
	hsh = { :tip_proc => tip_proc, :draw_proc => :rollback, :grayed_proc => grayed_proc }
	@palette.declare_button(:pal_back, hsh) { notify_proc.call :exec, ssb }
	
	ssv = :validate
	tip_proc = proc { notify_proc.call :tip, ssv }
	grayed_proc = proc { notify_proc.call :gray, ssv }
	hsh = { :tip_proc => tip_proc, :draw_proc => :valid, :grayed_proc => grayed_proc }
	@palette.declare_button(:pal_validate, hsh) { notify_proc.call :exec, ssv }

	ssvc = :next_curve
	tip_proc = proc { notify_proc.call :tip, ssvc }
	grayed_proc = proc { notify_proc.call :gray, ssvc }
	hsh = { :tip_proc => tip_proc, :draw_proc => :next_curve, :grayed_proc => grayed_proc }
	@palette.declare_button(:pal_next_curve, hsh) { notify_proc.call :exec, ssvc}
	
	#Buttons for options of each plugin
	@lst_plugins.each do |plugin|
		algo = @hsh_plugin_algo[plugin]
		@palette.set_current_family plugin
		algo.contribute_palette @palette
	end	
	
	#Associating the palette
	set_palette @palette
end
	
#PALETTE: Display the message in the palette zone and SU status bar
def palette_button_plugin(plugin, hshp)
	algo = @hsh_plugin_algo[plugin]
	return unless algo
	hinfo = F6_Curvizard.plugin_info[plugin]
	tip = (hinfo) ? T6[hinfo[:symb_tip]] : T6[hinfo[:symb_mnu]]
	pal_symb = ('pal_' + plugin.to_s).intern
	value_proc = proc { @plugin == plugin }
	draw_proc = algo.get_hparams[:draw_proc]
	draw_proc = algo.method("draw_button_opengl") unless draw_proc
	hsh = { :value_proc => value_proc, :tooltip => tip, :draw_proc => draw_proc }
	@palette.declare_button(pal_symb, hsh, hshp) { plugin_change plugin }
end
	
#PALETTE: Display the message in the palette zone and SU status bar
def palette_set_message(msg, color=nil)
	@palette.set_message msg, color
	Sketchup.set_status_text((msg) ? msg : "")
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 /pal_all_plugins/i
		pts = []
		pts.push Geom::Point3d.new(0, 0)
		pts.push Geom::Point3d.new(dx, 0)
		pts.push Geom::Point3d.new(dx, dy)
		pts.push Geom::Point3d.new(0, dy)
		lst_gl.push [GL_POLYGON, pts, 'blue']
		
	end	#case code
	
	lst_gl
end

#PALETTE: Custom drawing for the Blason
def draw_button_blason(symb, dx, dy)
	lst_gl = []
	dx2 = dx / 2
	dy2 = dy / 2
	pts = G6.pts_circle dx, 0, dx * 0.8, 24
	pts = pts[19..-2]
	lst_gl.push [GL_LINE_STRIP, pts, 'magenta', 3, '']
	lst_gl
end

#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
# ROLLBACK: Rollback and Undo management
#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------

#ROLLBACK: Clear the information
def rollback_clear
	@rollback_info = []
	@hsh_rollback_ids = {}
end

#ROLLBACK: Chekc if rollback is allowed
def rollback_allowed?
	!@rollback_info.empty?
end

#ROLLBACK: Clear the information
def rollback_register(ledges, parent, tr)
	lid = ledges.collect { |edge| edge.entityID }
	lid.each { |id| @hsh_rollback_ids[id] = id }
	@rollback_info.push [lid, parent, tr]
end

#ROLLBACK: Clear the information
def rollback_restore
	#Finding the previous edges by entity ID
	hsh_parents = {}
	hsh_edge_ids = {}
	@rollback_info.each do |a|
		lid, parent, tr = a
		next if hsh_parents[parent.entityID]
		hsh_parents[parent.entityID] = true
		entities = G6.grouponent_entities parent
		entities.each do |e|
			next unless e.instance_of?(Sketchup::Edge)
			id = e.entityID
			hsh_edge_ids[id] = e if @hsh_rollback_ids[id]
		end	
	end	
	
	#Reconstructing the list of edge groups
	roll_info = []
	@rollback_info.each do |a|
		lid, parent, tr = a
		ledges = lid.collect { |id| hsh_edge_ids[id] }
		roll_info.push [ledges.compact, tr, parent]
	end
	roll_info
end

#ROLLBACK: Execute the rollback
def rollback_execute(no_undo=false)
	if @flg_panel_abort
		@vpanel_abort.start T6[:T_STR_PleaseWait]
		@vpanel_abort.progression
	end	
	Sketchup.undo unless no_undo
	roll_info = rollback_restore
	set_state_mode :selection
	@selmode.reset
	roll_info.each { |a| @selmode.register_edges *a }
	rollback_clear
	results_clear
	xcurve_reset_all
	hilight_shut_down
	@vpanel_abort.stop if @flg_panel_abort
	onMouseMove_zero
end

#ROLLBACK: Execute the rollback
def retry_execute
	return unless results_previous?
	rollback_execute
	top_execute
end

#----------------------------------------------------------------
#----------------------------------------------------------------
# EXEC: Top execution
#----------------------------------------------------------------
#----------------------------------------------------------------

#EXEC: Add or remove entity from selection
def notify_edge_picked(action, ledges)
	if (action == :outside || action == :finish) && @selmode.empty?
		exit_tool
	elsif action == :finish
		top_execute
	end	
	ledges
end

#EXEC: Compute original contours
def store_original_contours(lst_info_contours)
	@original_contours = []
	lst_info_contours.each do |info|
		lvx, ledges, tr, parent = info
		@original_contours.push lvx.collect { |vx| tr * vx.position }
	end
end

#EXEC: Top Execution routine
def top_execute
	set_state_mode :computing
	
	lst_info_contours = @selmode.get_info_contours
	return false if lst_info_contours.length == 0
	
	#Making Group Unique to avoid further problems
	contour_convert_after_group_unique lst_info_contours, @suops
	
	#Register the original contours
	store_original_contours lst_info_contours
	
	#Process the geometry transformation
	begin
		geometry_generate lst_info_contours
	rescue Exception => e
		@algo.vbar_notify :end
		@suops.abort_operation
		Traductor::RubyErrorDialog.invoke e, @plugin_name, T6[:MSG_RubyError]		
		return exit_tool
	end
	
	onMouseMove_zero
	true
end

#Check groups belonging to the selection and make sure they are unique
def contour_convert_after_group_unique(lst_info_contours, suops)
	return false unless lst_info_contours && lst_info_contours.length > 0


	#Starting the Visual Bar
	delay = (lst_info_contours.length > 50) ? 0 : 0.5
	hsh = { :style_color => :bluegreen, :delay => delay, :mini_delay => 0 }
	vgbar = Traductor::VisualProgressBar.new T6[:T_VBAR_HandleDuplicatedGroups], hsh
	nbstep = 3
	istep = 0.0
	vgbar.start
	
	#Creating the mapping for non-unique groups
	hsh_not_uniq = {}
	hsh_mapid = {}
	n = lst_info_contours.length
	min = slice = 20
	mini = (n >= min && n >= 2*slice)
	vgbar.progression istep / nbstep, T6[:T_VBAR_UC_CheckDuplicate]
	lst_info_contours.each_with_index do |info, ik| 
		vgbar.mini_progression(ik * 1.0 / n, 1.0 / nbstep) if mini && ik.modulo(slice) == 0
		lvx, ledges, tr, parent = info
		next unless parent.instance_of?(Sketchup::Group)
		pid = parent.entityID
		next if hsh_not_uniq[pid] || G6.grouponent_unique?(parent)
		hsh_not_uniq[pid] = parent
		hsh_mapid[pid] = G6.grouponent_map_of_entities_object_id(parent)
	end
	if hsh_not_uniq.length == 0
		vgbar.stop
		return false
	end	
	
	#Make parent unique if required
	istep += 1
	nu = hsh_not_uniq.length
	vgbar.progression istep / nbstep, T6[:T_VBAR_UC_MakeUnique]
	suops.start_operation T6[:T_OPS_MakeGroupUnique]
	ik = 0
	hsh_not_uniq.each do |pid, parent|
		vgbar.mini_progression(ik * 1.0 / nu, 1.0 / nbstep)
		parent.entities.add_group.erase!
		ik += 1
	end

	#Reconstructing the list of edges and vertices when parents have been made unique
	istep += 1
	vgbar.progression istep / nbstep, T6[:T_VBAR_UC_ReconstructContours]
	lst_info_contours.each_with_index do |info, ik| 
		vgbar.mini_progression((ik) * 1.0 / n, 1.0 / nbstep) if mini && (ik == 0 || (ik).modulo(slice) == 0)
		lvx, ledges, tr, parent = info
		pid = parent.entityID
		next unless hsh_not_uniq[pid]
		entities = G6.grouponent_entities(parent)
		hmap = hsh_mapid[pid]
		ledges = ledges.collect { |e| entities[hmap[e.object_id]] }
		lvx = G6.vertices_from_edges_in_sequence(ledges)
		lst_info_contours[ik] = [lvx, ledges, tr, parent] 
	end
	
	#Commiting the operation
	suops.commit_operation

	#Stopping the visual bar
	vgbar.stop

	true
end

#EXEC: Common task to prepare execution 
def geometry_generate(lst_info_contours)
	results_clear
	#set_state_mode :computing
	
	#Regsitering the current edge for future restore
	rollback_clear
	@flg_panel_abort = (lst_info_contours.length > 30)
	lst_info_contours.each do |info|
		lvx, ledges, tr, parent = info
		rollback_register ledges, parent, tr
	end
	
	#Starting operation
	@suops.start_operation @title_ops
	
	#Executing the geometry with the plugin procedure
	@algo.vbar_notify :init, lst_info_contours.length
	results_info = @algo.execute_geometry lst_info_contours

	#Registering the results
	if results_info.empty?
		results_register_computed
	else
		results_info.each do |a| 
			ll, parent, tr = a
			ll.each { |lvx| results_register_vertices lvx, parent, tr }
		end	
	end
	
	#Finishing the operation
	@suops.commit_operation

	#Resetting the environment
	refresh_after_operation
	set_state_mode :selection
	@selmode.reset
	@hilight_on = true

	#Computing the display if required
	@algo.vbar_notify :dessin, 0
	dessin_compute
	@algo.vbar_notify :end
	
	if (@auto_exit =~ /C/ && @invoked_from == :contextual) || (@auto_exit =~ /P/ && @initial_selection_ok)
		if defined?(@view.refresh)
			@view.refresh
			sleep @exit_duration
		end
		@algo.vbar_notify :end
		exit_tool
	else		
		#@timer_highlight = UI.start_timer(@hilight_duration) { hilight_shut_down }
	end	
end

end	#End Class CVZ_Tool

end	#End Module F6_Curvizard
