=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Designed October 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			:   TopoShaper_Tool.rb
# Original Date	:   20 Oct 12 - version 1.0
# Description	:   TopoShaper Interactive Tool
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_TopoShaper

T6[:TIT_IsoOperation] = "TopoShaper: Terrain from Iso-contours"
T6[:TIP_GridLimits] = "Grid limits: Cell number X[%1, %2] Y[%3, %4] - Cell dimensions X[%5, %6] Y[%7, %8]"
T6[:TIP_GridLengthEquiv] = "Cell Dimensions: X = %1 Y = %2"
T6[:BOX_Grid_Title] = "Grid"

T6[:BOX_Mode_selection] = "SELECTION"
T6[:BOX_Mode_cleansing] = "CLEANSING"
T6[:BOX_Mode_algo] = "PREVIEW"
T6[:BOX_Mode_geometry] = "GENERATION"
T6[:TIP_Mode_selection] = "Step 1: Selection of contours"
T6[:TIP_Mode_cleansing] = "Step 2: Cleansing of contours"
T6[:TIP_Mode_algo] = "Step 3: Calculation of terrain and Preview"
T6[:TIP_Mode_geometry] = "Step 4: Generation of terrain"

T6[:TIP_Rollback_ToSelection] = "Go back to Contours Selection"
T6[:TIP_Rollback_ToCleansing] = "Go back to Contour Cleansing"
T6[:TIP_Rollback_ToAlgo] = "Go back to Terrain Preview"

T6[:BOX_OptionGeometry] = "Elements"
T6[:TIP_OptionGeometry] = "Options for generating the terrain"
T6[:TIP_OptionGeometryWall] = "Include / Exclude the Skirt"
T6[:TIP_OptionGeometryContour] = "Include / Exclude the Contours"
T6[:TIP_OptionGeometryMap] = "Include / Exclude 2D Contour Map"

T6[:TIP_OptionGeometryLabel] = "Include altitude labels in the contour map"
T6[:BOX_MapPosition] = "Map Position"
T6[:TIP_MapPosition] = "Position of the Contour Map: bottom or top"
T6[:TIP_MapPositionTop] = "On top of the Terrain"
T6[:TIP_MapPositionBottom] = "At bottom of the Terrain"
T6[:BOX_AltitudeLabel] = "Altitude Labels"
T6[:TIP_AltitudeLabel] = "Include Altitude labels on contours"
T6[:TIP_AltitudeLabelFactor] = "Scaling factor for the text size of Altitude Labels"
T6[:BOX_AltitudeLabelNoFilter] = "ALL"
T6[:TIP_AltitudeLabelNoFilter] = "NO FILTERING: Include labels for ALL contours"
T6[:TIP_AltitudeLabelFilter] = "FILTERING: Show only labels for contours with altitude which is a multiple of value"
T6[:PROMPT_AltitudeLabelFilter] = "Filtering value"

T6[:BOX_Hilltop] = "Hilltops"
T6[:TIP_HilltopMain] = "Global Option for extrapolating on hilltops and basins bottom"
T6[:TIP_HilltopFlatAll] = "Flat on ALL hilltops and basins"
T6[:TIP_HilltopFlat] = "Flat on selected hilltops and basins"
T6[:TIP_HilltopAutoAll] = "Rounding on ALL hilltops and basins"
T6[:TIP_HilltopAuto] = "Rounding on selected hilltops and basins"

T6[:BOX_Validate_selection] = "Cleanse | Contours"	
T6[:BOX_Validate_cleansing] = "Calculate | Terrain"	
T6[:BOX_Validate_algo] = "Generate | Terrain"	
T6[:TIP_Validate_selection] = "Validate selection and go to Contour Cleansing"	
T6[:TIP_Validate_cleansing] = "Go to the Calculation and Preview of the Terrain"	
T6[:TIP_Validate_algo] = "Generate the terrain as a SU Group"	
	
T6[:BOX_Cliff] = "Cliff"
T6[:TIP_CliffMain] = "Parameter to force cliff at contour (steeper slope)"
T6[:TIP_CliffNO] = "NO Cliff"
T6[:TIP_CliffVal] = "Steepness of the cliff (0 = No Cliff, 100% = Full Cliff)"
T6[:TIP_CliffTrue] = "TRUE Cliff"

T6[:BOX_WorkingView] = "Working View"	
T6[:TIP_WorkingView] = "Working view showing terrain and contours only"	
T6[:TIP_PreviewMode_InSitu] = "Native SU view in the model with focus on contours"	
T6[:TIP_PreviewMode_Surface] = "Working view centered around 3D Terrain"	
T6[:TIP_PreviewMode_Map] = "Working view centered around 2D Contour Map"	
T6[:TIP_PreviewMode_SurfaceMap] = "Working view showing both 3D Terrain and 2D Contour Map"	

T6[:BOX_SaveReplace] = "Update Contours & Reload"	
T6[:TIP_SaveReplace] = "Update contours by replacing existing ones, and Reload them"	
T6[:BOX_SaveCopy] = "Save Copy & Reload"	
T6[:TIP_SaveCopy] = "Save the updated contours as a copy in a group"	

T6[:BOX_AltitudeEdit] = "Edit | Altitude"
T6[:TIP_AltitudeEdit] = "Enter the Edition of Altitudes for contours"
T6[:TIT_FloatAltitudeEdit] = "Altitude Editor"
T6[:TIP_FloatAltitudeEdit] = "The Altitude Editor allows assignment of altitude to contours"

T6[:TIP_ContourAccept] = "Include the contour(s) in the terrain generation"
T6[:TIP_ContourIgnore] = "Exclude the contour(s) from the terrain generation"

T6[:TIP_EditTerrain] = "Click to edit Terrain"
T6[:MNU_EditTerrain] = "Edit Terrain"

T6[:TIP_DebugOption] = "Show contours used by each node of the mesh"
T6[:ERROR_CannotLoadAttr] = "ERROR: Cannot load the Terrain"

T6[:OPS_UC_UpdateTitle] = "TOPOSHAPER: Update contours"
T6[:VBAR_UC_UpdateTitle] = "Updating Contours Geometry"
T6[:VBAR_UC_CheckDuplicate] = "Checking duplicated groups"
T6[:VBAR_UC_MakeUnique] = "Making groups Unique"
T6[:VBAR_UC_HandlingUnique] = "Handling Entities of Unique groups"
T6[:VBAR_UC_EraseNeedleEyes] = "Erasing Needle Eyes"
T6[:VBAR_UC_EraseEdges] = "Erasing Hooks and Modified edges"
T6[:VBAR_UC_CreateNewEdges] = "Generating the updated contours"
T6[:VBAR_UC_RegisterContours] = "Registering the contours"

T6[:MSG_RubyError] = "The operation is fully aborted"	
T6[:BOX_ClickToExit] = "Click to Exit"
T6[:ERR_NoEditInSitu] = "Contours cannot be Edited in-situ"
T6[:ERR_SwitchToWorking] = "Please Switch to Working View"

#Structure to hold information on the original contours
Contour3D = Struct.new :inum, :pts, :lvx, :ledges, :tr, :parent

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

#Actual launching of menus	
def F6_TopoShaper.action__mapping(action_code)
	case action_code
	when :quad_terrain, :cleanser
		Sketchup.active_model.select_tool TopoShaperTool.new(action_code)
	when :cloud
		Sketchup.active_model.select_tool TopoShaperCloudTool.new(action_code)
	end	
end

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

class TopoShaperTool < Traductor::PaletteSuperTool

@@dico = "TOPOSHAPER"
@@dico_grid = "Grid"

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

def initialize(action_code, *args)
	#Loading parameters
	options_load_global

	#Basic initialization
	@model = Sketchup.active_model
	@tr_id = Geom::Transformation.new
	@title = "TopoShaper"
	@action_code = action_code
	
	#Parsing the arguments
	args.each { |arg| arg.each { |key, value|  parse_args(key, value) } if arg.class == Hash }

	#Static initialization
	init_cursors
	init_colors
	init_messages

	#Initializating the Edge Picker
	init_edge_picker
	
	#Creating the palette manager and texts
	init_palette
	
	#Creating the Zoom artefact
	@zoomar = Traductor::ZoomArtefact.new
	
	@use_working_view = true
	
	#Callback when changing default parameters
	MYDEFPARAM.add_notify_proc(self.method("notify_from_defparam"), true)		
end

#INIT: Notification from default parameters
def notify_from_defparam(key, val)
	@algo.colors_init if @algo
	@cleanser.colors_init if @cleanser
	@view.invalidate
end

#TOPOSHAPER_TOOL: 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	

#---------------------------------------------------------------------------------------------
# OPTIONS: Option Management and storage
#---------------------------------------------------------------------------------------------
				
#OPTIONS: Load options from registry
def options_load_global
	#Global GUI parameters for TopoShaper
	sparam = Sketchup.read_default "TopoShaper", "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: 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 "TopoShaper", "Options", sparam	
end
		
#INIT: Initialize texts and messages
def init_messages
	@mnu_exit = T6[:T_BUTTON_Exit]	
	@mnu_abort = T6[:T_STR_AbortTool]
	@tip_validate = @title	
	@tip_rollback_to_selection = T6[:TIP_Rollback_ToSelection]
	@tip_rollback_to_cleansing = T6[:TIP_Rollback_ToCleansing]
	@tip_rollback_to_algo = T6[:TIP_Rollback_ToAlgo]
	@mnu_validate_selection = T6[:TIP_Validate_selection]	
	@mnu_validate_cleansing = T6[:TIP_Validate_cleansing]
	@mnu_validate_algo = T6[:TIP_Validate_algo]	
	
	@tip_next_curve = T6[:TIP_NextCurve]
	@tip_exec_geometry = T6[:TIP_ExecGeometry]	
	@tip_edit_terrain = T6[:TIP_EditTerrain]
	
	@tip_click_to_exit = T6[:BOX_ClickToExit]
	
	@tip_no_edition_in_situ = T6[:ERR_NoEditInSitu] + "\n" + T6[:ERR_SwitchToWorking]

	@hsh_boxinfo_exit = { :bk_color => 'lightgreen', :fr_color => 'green' }					 	
	@hsh_boxinfo_warning = { :bk_color => 'yellow', :fr_color => 'red' }					 	
end

#INIT: Initialize colors
def init_colors
	@color_message = 'khaki'
	@hsh_boxinfo_tip = { :bk_color => 'lightyellow', :fr_color => 'yellow' }					 
end

#INIT: Initialize cursors
def init_cursors
	@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_green = Traductor.create_cursor "Cursor_hourGlass_Green", 16, 16		
	@id_cursor_hourglass_red = Traductor.create_cursor "Cursor_hourGlass_Red", 16, 16		
	@id_cursor = @id_cursor_default
end

#INIT: Initializating the Edge Selection Picker
def init_edge_picker
	hsh = {}
	hsh[:notify_proc] = self.method 'notify_edge_picked'
	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

#--------------------------------------------------
# Activation / Deactivation
#--------------------------------------------------

#ACTIVATION: Tool activation
def activate
	LibFredo6.register_ruby "TopoShaper::IsoContours"
	Traductor::Hilitor.stop_all_active
	@model = Sketchup.active_model
	@model_current = @model if @model != @model_current
	@selection = @model.selection
	@entities = @model.active_entities
	@view = @model.active_view
		
	#Recovering problems in previous sessions
	recover_problems_with_previous_sessions
	
	#Resetting the environment
	reset_context
	
	#Initiating the palette
	set_state_mode :selection

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

	#Handling the initial selection
	handle_initial_selection
		
	refresh_viewport	
	#@view.invalidate
	#show_message
end

#ACTIVATION: Recover problems with previous sessions
def recover_problems_with_previous_sessions
	#For Zoom artefacts
	Traductor::TempStuff.cleanup
	
	#for Views
	vdesc = @view.camera.description
	if vdesc && vdesc =~ /TOPOSHAPER/
		@view.camera.description = ""
	end	
end

#ACTIVATION: Reset context variables and environment
def reset_context
	@button_down = false
	@ip = Sketchup::InputPoint.new
	@ph = @view.pick_helper
	@preview_in_situ = true
end

#ACTIVATION: Tool Deactivation
def deactivate(view)
	GridDimensionDialog.close
	restore_view unless @aborting
	if @mode == :geometry && !@aborting
		@suops.finish_operation
	else
		restore_view
		@suops.abort_operation
	end	
	options_save
	registry_info_save
	selection_at_exit
	view.invalidate
end

#ACTIVATION: Manage the initial selection or the previous results
def handle_initial_selection
	@old_selection = @selection.to_a.clone
	return false if @selection.empty?
	
	#Selection of an existing terrain group
	lst_groups = @selection.grep(Sketchup::Group)
	lst_groups.each do |group|
		@type_attr = attribute_group?(group)
		if @type_attr
			@group_attr = group
			@tr_attr = group.transformation
			@initial_selection_ok = true
			algo_load_from_attr
			return
		end
	end
	
	#Selection by Edge picker
	@handling_initial_selection = true
	@selmode.check_initial_selection
	@lst_info_contours = @selmode.get_info_contours
	@handling_initial_selection = false
	return false if @lst_info_contours.length == 0
	@initial_selection_ok = true
	execute_validate
end
	
#ACTIVATION: Set / reset the selection at exit
def selection_at_exit
	sel = @old_selection.find_all { |a| a.valid? && a.instance_of?(Sketchup::Edge) } if @old_selection
	@selection.add sel unless sel.empty?
end
		
#ACTIVATION: Exit the tool
def exit_tool
	@aborting = false
	unless @mode == :geometry
		restore_view
		@suops.abort_operation
	end	
	@model.select_tool nil
end

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

#Contextual menu
def getMenu(menu)
	cxmenu = Traductor::ContextMenu.new
	
	if @mode == :algo
		@algo.contextual_menu_contribution(cxmenu)
	elsif @mode == :cleansing	
		@cleanser.contextual_menu_contribution(cxmenu)
	elsif @mode == :selection
		if @group_attr
			cxmenu.add_item(T6[:MNU_EditTerrain]) { algo_load_from_attr }
		else
			@selmode.contextual_menu_contribution(cxmenu)
		end	
	end
		
	#Menu Validate and Rollback
	cxmenu.add_sepa
	
	unless notify_from_palette(:gray, :validate) || (@group_attr && @mode == :selection)
		tip = notify_from_palette :tip, :validate
		cxmenu.add_item(tip) { notify_from_palette :exec, :validate }
	end
	unless notify_from_palette :gray, :back
		tip = notify_from_palette :tip, :back
		cxmenu.add_item(tip) { notify_from_palette :exec, :back }
	end

	#Exit
	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

#--------------------------------------------------
# State machine and Top Execution
#--------------------------------------------------

#EXEC: Set the current mode
def set_state_mode(mode)
	@mode = mode
	@palette.visible_family @mode
	@myclass = nil
	case @mode
	when :selection
		@mode_text = T6[:BOX_Mode_selection]
		@mode_tip = T6[:TIP_Mode_selection]	
	when :cleansing
		@algo.edgewire_erase if @algo
		@myclass = @cleanser
		@cleanser.reset_editors
		@mode_text = T6[:BOX_Mode_cleansing]
		@mode_tip = T6[:TIP_Mode_cleansing]
	when :algo
		@cleanser.edgewire_erase if @cleanser
		@myclass = @algo
		@mode_text = T6[:BOX_Mode_algo]
		@mode_tip = T6[:TIP_Mode_algo]
	when :geometry
		@algo.edgewire_erase if @algo
		@mode_text = T6[:BOX_Mode_geometry]
		@mode_tip = T6[:TIP_Mode_geometry]	
	else
		@mode_text = ""
		@mode_tip = ""	
	end	
	refresh_viewport
end

#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
		after_selection
	end	
	ledges
end

def ask_grid_dimension
	GridDimensionDialog.invoke @algo, self
end

def notify(action, *args)
	case action
	when :grid_dimension
		if args[0]
			@algo_processing = true
			@algo.notify_grid_dimension *args
			@algo_processing = false
			set_state_mode :algo
		end	
		
	when :infobox
		set_infobox *args
	end	
end

#Manage action after end of selection
def after_selection
	@lst_info_contours = @selmode.get_info_contours
	register_contours
	
	#Cleansing the contours
	cleansing_processing
end

#Rescue from Ruby errors and abort
def rescue_errors(exception, title)
	Traductor::Hilitor.stop_all_active
	Traductor::Hilitor.stop_all_active
	Traductor::RubyErrorDialog.invoke exception, title, T6[:MSG_RubyError, "TopoShaper"]
	abort_tool
end

#EXEC: Execute the Cleansing
def cleansing_processing
	picture_load
	@cleansing_processing = true
	begin
		hsh = { :palette => @palette }
		lst_pts = (@selmode.empty?) ? @algo.get_lst_pts : @lst_pts
		@cleanser = TopoShaperCleanser.new(self, lst_pts, hsh)
		set_state_mode :cleansing
		status = @cleanser.top_processing @saved_contours
		set_preview_working(:map)
	rescue  Exception => e
		rescue_errors e, "CLEANSING"
	end	
	@cleansing_processing = false
	onMouseMove_zero
	onSetCursor
end

#EXEC: Execute the calculation of terrain
def algo_processing
	@algo_processing = true
	unless @cleanser
		hsh = { :palette => @palette, :group_attr => @group_attr, :tr_attr => @tr_attr }
	else
		hsh = { :palette => @palette, :plane_normal => Z_AXIS, :lst_pts => @cleanser.get_lst_pts }
	end	
	
	#Initializing the class instance for interactive mode
	@algo = TopoShaperAlgo.new
	@algo.initialize_interactive self, @hsh_options_global, hsh
	
	if @algo.creation_invalid?
		UI.messagebox T6[:ERROR_CannotLoadAttr]
		abort_tool
		return
	end
	@myclass.edgewire_erase if @myclass
	@myclass = @algo
	begin
		@algo.top_processing
		(@use_working_view) ? set_preview_working(:surface) : switch_view
	rescue Exception => e
		rescue_errors e, "CALCULATING TERRAINS"
	end	
	@algo_processing = false
	set_state_mode :algo
	@myclass.edgewire_show if working_view_active?
	onMouseMove_zero
	onSetCursor
end

#ATTR: Erase the attribute group if any
def attribute_group_retrieve
	return nil unless @entity_attr
	@model.active_entities.grep(Sketchup::Group).find { |g| g.entityID == @entity_attr }
end

#EXEC: Load a terrain from a group already constructed
def algo_load_from_attr
	@entity_attr = @group_attr.entityID
	@camera_last_working = nil
	algo_processing
end

#EXEC: Load a terrain from a group already cleansed
def cleansing_load_from_attr
	@entity_attr = nil
	@camera_last_working = nil
	cleansing_processing
end

#ROLLBACK: Check if rollback is allowed
def rollback_allowed?
	return false if @mode == :selection || (@mode == :cleansing && @group_attr)
	true
end

#ROLLBACK: Execute the rollback
def rollback_execute(no_undo=false)
	if @mode == :cleansing
		restore_view
		set_state_mode :selection
		@suops.abort_restart_operation
	elsif @mode == :algo
		if @cleanser
			set_state_mode :cleansing
			set_preview_working(:map) if @use_working_view	
			if working_view_active?
				@myclass.edgewire_show
			end	
			@myclass.compute_infobox
		else
			cleansing_processing
		end	
	elsif @mode == :geometry
		geometry_rollback
		set_state_mode :algo
		set_preview_working(:surface) if @use_working_view	
		@myclass.edgewire_show if working_view_active?
		@myclass.compute_infobox
	end	
	onMouseMove_zero
end

#Check if current contour can be validated
def validate_allowed?
	@group_attr || !(@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
	when :cleansing
		begin
			@cleanser.top_repairing
			set_preview_working(:surface)
		rescue Exception => e
			rescue_errors e, "REPAIRING CONTOURS"
		end
		algo_processing
	when :algo
		geometry_execute
	when :execute
		exit_tool
	end	
end

#Update the contour geometry
def save_copy_contours
	puts "Copy contours not yet implemented"
end
	
def save_replace_contours
	begin
		@cleanser.top_repairing
		status = geometry_update_contours
	rescue Exception => e
		rescue_errors e, "SAVE CONTOUR GEOMETRY"
	end
	if status
		@saved_contours = true
		cleansing_processing
	end	
end

#Abort the operation and exit
def abort_tool
	@aborting = true
	restore_view
	@suops.abort_operation
	@model.select_tool nil
end

#---------------------------------------------------------------------------------------------
# SU CONTOURS: Update contour sand reconstruct the 3D
#---------------------------------------------------------------------------------------------

#Update the geometry of contours
def geometry_update_contours
	return false unless @lst_info_contours
	
	#Getting the bad edges from the Edgepicker
	hbad_info = (@selmode) ? @selmode.get_bad_edges_info : []
	
	#Calculating the mapping between old and new contours
	lst_map = @cleanser.save_replace_contours
	return false if lst_map.compact.length == 0

	#Starting the Visual Bar
	hsh = { :style_color => :bluegreen }
	vgbar = Traductor::VisualProgressBar.new T6[:VBAR_UC_UpdateTitle], hsh
	nbstep = 7
	istep = 0.0
	vgbar.start
	
	#Updating the contour geometry
	@suops.abort_operation
	@suops.start_operation T6[:OPS_UC_UpdateTitle]
	
	#Check if some parents are not unique and store edge transposition info
	vgbar.progression istep / nbstep, T6[:VBAR_UC_CheckDuplicate]
	hsh_not_uniq = {}
	hsh_mapid = {}
	n = @lst_info_contours.length
	min = slice = 20
	mini = (n >= min && n >= 2*slice)
	@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
		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
	
	#Make parent unique if required
	istep += 1
	vgbar.progression istep / nbstep, T6[:VBAR_UC_MakeUnique]
	hsh_not_uniq.each do |pid, parent|
		G6.grouponent_make_unique parent
	end

	#Reconstructing the list of edges and vertices when parents have been made unique
	istep += 1
	vgbar.progression istep / nbstep, T6[:VBAR_UC_HandlingUnique]
	@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
	
	#Erasing the bad edges
	istep += 1
	vgbar.progression istep / nbstep, T6[:VBAR_UC_EraseNeedleEyes]
	hbad_info.each do |pid, a|
		parent, lbad_edges = a
		entities = G6.grouponent_entities(parent)
		hmap = hsh_mapid[pid]
		lbad_edges = lbad_edges.collect { |e| entities[hmap[e.object_id]] } if hmap
		entities.erase_entities lbad_edges if lbad_edges.length > 0
	end
		
	#Registering the layers
	contour_info_edge = @lst_info_contours.collect { |info| e = info[1].first ; [e.layer, e.material] }
	
	#Erasing all required edges
	istep += 1
	vgbar.progression istep / nbstep, T6[:VBAR_UC_EraseEdges]
	new_info = []
	@lst_info_contours.each_with_index do |info, ik| 
		lvx, ledges, tr, parent = info
		entities = G6.grouponent_entities(parent)
		pts = lst_map[ik]		
		if pts == nil
			new_info.push info
		else
			ledges = ledges.find_all { |e| e.valid? }
			entities.erase_entities ledges
		end	
	end	
	
	#Creating the updated contours
	istep += 1
	vgbar.progression istep / nbstep, T6[:VBAR_UC_CreateNewEdges]
	@lst_info_contours.each_with_index do |info, ik| 
		lvx, ledges, tr, parent = info		
		pts = lst_map[ik]
		next if pts == :delete || pts == nil
		entities = G6.grouponent_entities(parent)
		t = tr.inverse
		edges = entities.add_curve pts.collect { |pt| t * pt }
		layer_ori, mat_ori = contour_info_edge[ik]
		edges.each { |e| e.layer = layer_ori ; e.material = mat_ori }
		lvx = G6.vertices_from_edges_in_sequence(edges)
		info = [lvx, edges, tr, parent]
		new_info.push info
	end	
	@suops.finish_operation
	@suops.start_operation
	
	#Updating the registry info
	registry_info_save
	nbtot_contours = new_info.length
	nbtot_points = 0
	new_info.each { |info|	nbtot_points += info[0].length }
	registry_info_clone nbtot_contours, nbtot_points
	registry_info_save
	
	#Registering the contours
	istep += 1
	vgbar.progression istep / nbstep, T6[:VBAR_UC_RegisterContours]
	@lst_info_contours = new_info
	register_contours
	@selmode.reset_selection
	@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		
		@selmode.register_edges ledges, tr, parent
	end	
	
	#Stopping the visual bar
	vgbar.stop
	
	true
end

def save_attribute

end

#---------------------------------------------------------------------------------------------
# CONTOUR MANAGEMENT: 
#---------------------------------------------------------------------------------------------

#Creating a contour structure
def register_contours
	@lst_pts = []
	@nbtot_points = 0
	@lst_info_contours.each_with_index do |info, ik| 
		lvx, ledges, tr, parent = info
		@lst_pts[ik] = lvx.collect { |vx| tr * vx.position }
		@nbtot_points += lvx.length
	end	
	@nbtot_contours = @lst_pts.length
	registry_info_load
end

#---------------------------------------------------------------------------------------------
# SU TOOL: Mouse Movement Methods
#---------------------------------------------------------------------------------------------

#SU TOOL: 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
		#view.invalidate
		#show_message
		refresh_viewport
		return
	end	
	@mouse_in_palette = false
	#reconcile_button_state(flags, x, y, view)
	
	#Synchronize draw and move
	return if @moving
	@moving = true

	#Dispatching the event
	case @mode
	when :selection	
		@group_attr, @tr_attr, @type_attr = attribute_under_mouse(flags, x, y, view)
		@selmode.onMouseMove(flags, x, y, view) unless @group_attr
	when :algo, :cleansing
		if @myclass
			@myclass.mouse_on_move(flags, x, y, view)
			@zoomar.move_mark @myclass.zoom_target
		end	
	end
	
	#Refreshing the view
	refresh_viewport
	#view.invalidate
	#show_message
end	

#SU TOOL: Check if the state of mouse is compatible with internal flags
def reconcile_button_state(flags, x, y, view)
	if RUN_ON_MAC
		onLButtonUp(flags, x, y, view) if flags == 256 && @button_down
	else
		onLButtonUp(flags, x, y, view) if flags == 1 && !@button_down
	end
end

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

def onMouseEnter(view)
	@mouseOut = false
	view.invalidate
end
	
def attribute_group?(group)
	return nil unless group && group.attribute_dictionary("TOPOSHAPER", false)
	(group.get_attribute(@@dico, @@dico_grid) ? :algo : :cleanser)
end

def attribute_under_mouse(flags, x, y, view)
	@ph.do_pick x, y
	elt = [@ph.picked_face, @ph.picked_edge].find { |e| e }
				
	#Finding the parent and transformation
	tr = @tr_id
	parent = nil
	grand_parent = nil
	return nil unless elt
	for i in 0..@ph.count
		ls = @ph.path_at(i)
		if ls && ls.include?(elt)
			parent = ls[-2]
			grand_parent = ls[-3]
			tr = @ph.transformation_at(i)
			break
		end
	end	
	return nil unless parent && parent.instance_of?(Sketchup::Group)
	
	type_attr = attribute_group?(parent)
	return [parent, tr, type_attr] if type_attr
	
	type_attr = attribute_group?(grand_parent)
	return [grand_parent, parent.transformation * tr, type_attr] if type_attr

	nil
end
		
#---------------------------------------------------------------------------------------------
# SU TOOL: Click Management
#---------------------------------------------------------------------------------------------

#SU TOOL: Button click DOWN
def onLButtonDown(flags, x, y, view)
	#Interrupting geometry construction if running
	return if @suops.interrupt?

	#Palette management
	return if super
	
	#Dispatching the event
	case @mode
	when :selection
		@selmode.onLButtonDown(flags, x, y, view) unless @group_attr
	when :algo, :cleansing
		@myclass.onLButtonDown(flags, x, y, view)
		#execute_validate
	when :geometry
		exit_tool
	end	
	onMouseMove_zero
end

#SU TOOL: Button click UP - Means that we end the selection
def onLButtonUp(flags, x, y, view)
	return if super
	case @mode
	when :selection	
		if @group_attr
			algo_load_from_attr
		else
			@selmode.onLButtonUp(flags, x, y, view)
		end	
	when :algo, :cleansing
		@myclass.onLButtonUp(flags, x, y, view)
	end	
	@button_down = false
	onMouseMove_zero
end

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

#---------------------------------------------------------------------------------------------
# SU TOOL: Keyboard handling
#---------------------------------------------------------------------------------------------

#Return key pressed
def onReturn(view)
	execute_validate
end

def ctrl_down? ; @ctrl_down ; end
def shift_down? ; @shift_down ; end

#SU TOOL: Handle Key down
def onKeyDown(key, rpt, flags, view)
	key = Traductor.new_check_key key, flags, false
	
	#Interrupting geometry construction if running
	return if @suops.interrupt?
		
	#Notifying the cleanser
	return if @mode == :cleansing && @cleanser.notify_key_down(key)
	
	@num_keydown = 0 unless @num_keydown
	@num_keydown += 1
	case key			
	when CONSTRAIN_MODIFIER_KEY
		@shift_down = true
		
	when COPY_MODIFIER_KEY
		@ctrl_down = @num_keydown
		onMouseMove_zero 1
			
	when 13
		if @shift_down || @ctrl_down
			
		end	
			
	else	
		@shift_down = @ctrl_down = false
	end	
end

#SU TOOL: Key UP received
def onKeyUp(key, rpt, flags, view)
	key = Traductor.new_check_key key, flags, true
	
	#Dispatching the event
	case key	
	when VK_DELETE, 8
		#geometry_rollback
		
	when CONSTRAIN_MODIFIER_KEY
		@time_shift_up = Time.now
		@shift_down = false
		
	when COPY_MODIFIER_KEY
		@time_ctrl_up = Time.now
		if @ctrl_down && @ctrl_down == @num_keydown

		end	
		onMouseMove_zero 0
		@ctrl_down = false
		
	else 
		if @time_ctrl_up && (Time.now - @time_ctrl_up) < 0.1	
			onMouseMove_zero 0
		end	
		if @time_shift_up && (Time.now - @time_shift_up) < 0.1	
			onMouseMove_zero 0
		end	
		@shift_down = @ctrl_down = false
	end	
end

#---------------------------------------------------------------------------------------------
# SU TOOL: Drawing Methods
#---------------------------------------------------------------------------------------------
 
#SU TOOL: Draw top method
def draw(view)	
	#Drawing the curves selected
	case @mode
	when :selection
		if @group_attr
			drawing_group_attr view
		else	
			@selmode.draw view unless @handling_initial_selection
		end	
	when :cleansing, :algo
		drawing_preview view
		if @myclass && !@mouse_in_palette && !@mouseOut
			if @mode == :cleansing && @preview_in_situ
				G6.draw_rectangle_multi_text view, @xmove, @ymove, @tip_no_edition_in_situ, @hsh_boxinfo_warning
			else
				@myclass.dessin_boxinfo(view, @xmove, @ymove)
			end	
		end	
	when :geometry
		G6.draw_rectangle_multi_text view, @xmove, @ymove, @tip_click_to_exit, @hsh_boxinfo_exit if !@mouse_in_palette && !@mouseOut
	end	
	@moving = false
	
	#Drawing the palette
	super
end

def drawing_preview(view)
	@myclass.dessin view	
end

#Draw the selection box for Terrain group
def drawing_group_attr(view)
	return unless @group_attr && @group_attr.valid?
	view.line_stipple = ''
	view.line_width = 3
	view.drawing_color = 'red'
	llines = G6.grouponent_box_lines(view, @group_attr, @tr_attr)
	view.draw GL_LINES, llines unless llines.empty?
	
	G6.draw_rectangle_multi_text view, @xmove, @ymove, @tip_edit_terrain, @hsh_boxinfo_tip
end

def getExtents
	(@myclass && @myclass.bb_extents) ? @myclass.bb_extents : @model.bounds
	@model.bounds
end

def suspend(view)
	if @mode == :algo || @mode == :cleansing
		@update_busy = true
		@myclass.edgewire_show if working_view_active?
	end
end

def resume(view)
	return refresh_viewport unless @myclass && (@mode == :algo || @mode == :cleansing)
	#return view.invalidate unless @myclass && (@mode == :algo || @mode == :cleansing)
	if @mode == :algo
		@zoomar.move_mark @myclass.zoom_target
		@myclass.edgewire_hide if working_view_active?
	end	
	@myclass.dessin_refresh_when_view_changed
	update_dessin_on_resume
end

def update_dessin_on_resume
	if @update_busy
		@update_busy = false
		@update_timer = UI.start_timer(0.5) { update_dessin_on_resume } unless @update_timer
	else	
		@update_timer = nil
		@update_busy = false
		@myclass.dessin_update_when_view_changed_async
	end	
	refresh_viewport
	#@view.invalidate
end
#---------------------------------------------------------------------------------------------
# SU TOOL: Show Messages
#---------------------------------------------------------------------------------------------

def onCancel(flag, view)
	if flag == 0		#Escape
		case @mode
		when :cleansing
			if @cleanser.handle_escape
				onMouseMove_zero
				return
			end	
		when :algo
			rollback_execute true
		end	
	else				#Undo
	
	end
	onMouseMove_zero
end

#SU TOOL: Computing Current cursor
def onSetCursor
	#Hourglass cursors
	@id_cursor = 0
	if @algo_processing	|| @cleansing_processing
		@id_cursor = @id_cursor_hourglass_red
	elsif @geometry_processing
		@id_cursor = @id_cursor_hourglass_green
	end	
	return UI.set_cursor(@id_cursor) if @id_cursor != 0

	#Palette cursors
	ic = super
	return (ic != 0) if ic
	
	#Other cursors depending on state
	case @mode
	when :cleansing
		@id_cursor = @cleanser.onSetCursor
	when :algo
		@id_cursor = 0
	when :geometry
		@id_cursor = @id_cursor_arrow_exit
	else
		@id_cursor = @id_cursor_default
	end	
	@id_cursor = @id_cursor_default if @id_cursor == 0
	UI.set_cursor @id_cursor
end

#SU TOOL: 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
	end	
	
	msg = ""
	msg = @myclass.palette_message if @myclass
	
	#Displaying the message
	@palette.set_message msg, @color_message
	Sketchup.set_status_text((msg) ? msg : "")
	#palette_set_message text, @color_message
end

#SU TOOL: 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 : "")
	@view.invalidate
end

#SU TOOL: Display the message in the palette zone and SU status bar
def palette_set_tooltip(msg, level=nil)
	@palette.set_tooltip msg, level
end

#TOOL: Set the text for the information box in the palette
def set_infobox(text1, text2=nil, level=nil)
	@info_text1 = text1 if text1
	@info_text2 = text2 if text2
	text = @info_text1 + "\n" + @info_text2
	@palette.set_tooltip text, level
end

#TOOL: refresh the viewport
def refresh_viewport
	show_message
	@view.invalidate
	onSetCursor
end
	
#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
# GEOMETRY: Mouse Movement Methods
#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------

#GEOMETRY: Execute the generation of Terrain geometry
def geometry_execute
	@geometry_processing = true
	restore_view
	@suops.abort_operation
	
	begin
		@algo.geometry_execute @suops, attribute_group_retrieve()
	rescue Exception => e
		rescue_errors e, "GENERATION OF TERRAIN"
	end
end

#GEOMETRY: Rollback the generation
def geometry_rollback
	case @mode
	when :algo
		restore_view
		@suops.abort_restart_operation
		set_state_mode :cleansing
	when :geometry
		@suops.abort_restart_operation
		set_state_mode :algo
		onMouseMove_zero
	end	
end

#GEOMETRY: Notification of the Termination of the geometry
def geometry_terminate(time)
	@geometry_processing = false
	@algo.geometry_terminate
	if time < 0
		set_state_mode :algo
	else
		set_state_mode :geometry
	end
	onMouseMove_zero
	onSetCursor
end

#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
# VCB: Handling VCB inputs
#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
	
def enableVCB? ; @mode == :algo || (@mode == :cleansing && @cleanser.enableVCB?) ; end
	
def onUserText(text, view)
	case @mode
	when :algo
		@algo.handle_VCB(text, view)
	when :cleansing
		@cleanser.handle_VCB(text, view)
	end	
	#@view.invalidate
	refresh_viewport
end	

#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
# VISU: Management of Insitu and Working views
#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------

#VISU: View requested is in_situ
def set_preview_in_situ
	return unless working_view_active?
	@camera_last_working = G6.camera_duplicate @view.camera
	@preview_in_situ = true
	@view.camera = @camera_last_in_situ, 0
	change_view :in_situ
end

#VISU: View requested is a working view
def set_preview_working(symb=nil)
	in_situ = !working_view_active?
	if @camera_last_in_situ
		@camera_last_in_situ = G6.camera_duplicate(@view.camera) unless working_view_active?
	else	
		@camera_original = G6.camera_duplicate(@view.camera) unless working_view_active?
		@camera_last_in_situ = (MYDEFPARAM[:TPS_OPTION_ZoomInSitu]) ? @myclass.camera(:geometry) : @camera_original
	end	
	@camera_last_in_situ.description = ""
	@preview_in_situ = false
	change_view :working
	@view.camera = ((!symb && @camera_last_working) ? @camera_last_working : @myclass.camera(symb)), 0
	@myclass.dessin_refresh_when_view_changed
	update_dessin_on_resume
end

#VISU: Swicth to the working view if necessary
def switch_view
	return if working_view_active? || @preview_in_situ
	set_preview_working
end

#VISU: Restore the full SU View
def restore_view
	return unless @myclass
	change_view :restore
	if working_view_active?	
		@camera_last_working = G6.camera_duplicate @view.camera
		@view.camera = (@aborting) ? @camera_original : @camera_last_in_situ
	end
end

#VISU: Check if the current view is a working view
def working_view_active?
	@view.camera.description =~ /TOPOSHAPER_/	
end

def change_view(view_state)
	@temp_layer = Traductor::TemporaryLayer.new unless @temp_layer
	if view_state == :restore || view_state == :in_situ
		@temp_layer.restore
	else
		@myclass.edgewire_set_layer @temp_layer.layer
		@temp_layer.switch_to_temp_layer
	end
	@myclass.notify_working_view view_state
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# REGISTRY: Managing recording of parameters in Registry
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

#REGISTRY: get the registry information hash table
def registry_info_get
	(@index_info_registry) ? @lst_info_registry[@index_info_registry] : registry_info_new
end

def registry_info_set(symb, val)
	hreg = (@index_info_registry) ? @lst_info_registry[@index_info_registry] : registry_info_new
	hreg[symb] = val
end
	
def registry_info_toggle(symb)
	hreg = (@index_info_registry) ? @lst_info_registry[@index_info_registry] : registry_info_new
	hreg[symb] = !hreg[symb]
end
	
#REGISTRY: Load information from registry about altitude settings and 
#          check if applicable to the contour to set the default values
def registry_info_load
	@nb_info_registry = 30
	@lst_info_registry = []
	for i in 0..@nb_info_registry-1
		sparam = Sketchup.read_default "Fredo6_TopoShaper", "Info_Terrain__#{i}"
		break unless sparam
		begin
			hparam = eval sparam
			@lst_info_registry.push hparam
		rescue
			
		end
	end
	
	#Matching the registry
	@index_info_registry = nil
	@lst_info_registry.each_with_index do |hreg, ik|
		nbtot_contours = hreg[:nbtot_contours]
		next if nbtot_contours != @nbtot_contours
		nbtot_points = hreg[:nbtot_points]
		next if nbtot_points != @nbtot_points
		@index_info_registry = ik
		break
	end
end

#REGISTRY: Save information from registry about altitude settings for future retrieval
def registry_info_save
	return unless @nb_info_registry
	for i in 0..@nb_info_registry-1
		lreg = @lst_info_registry[i]
		break unless lreg
		sparam = lreg.inspect.gsub(/"/, "'")
		Sketchup.write_default "Fredo6_TopoShaper", "Info_Terrain__#{i}", sparam
	end	
end

#REGISTRY: Contributing to the registry
def registry_info_contribute(hsh)
	#Creating the entry in registry if not already there
	if @index_info_registry
		hreg = @lst_info_registry[@index_info_registry]
	else
		hreg = registry_info_new
	end	
	
	#Contributing
	hsh.each { |key, val| hreg[key] = val }
end

#REGISTRY: Create a new entry in the registry list
def registry_info_new
	hreg = { :nbtot_contours => @nbtot_contours, :nbtot_points => @nbtot_points }
	@lst_info_registry = [] unless @lst_info_registry
	@lst_info_registry.unshift hreg
	@index_info_registry = 0
	hreg
end

def registry_info_clone(nbtot_contours, nbtot_points)
	hreg_cur = registry_info_get
	hreg = registry_info_new
	hreg_cur.each { |key, val| hreg[key] = val }
	hreg[:nbtot_contours] = nbtot_contours
	hreg[:nbtot_points] = nbtot_points
	hreg
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# PICTURE: Manage the picture background map
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

def picture_load
	hsh = registry_info_get
	@picture_file = hsh[:picture_file]
	@picture_visible = hsh[:picture_visible]
	@picture_transform = hsh[:picture_transform]
end

#PICTURE: Check if a picture map is currently defined
def picture_file
	@picture_file
end

#PICTURE: ask for a picture file via an Open panel
def picture_visible? ; @picture_visible ; end
def picture_toggle
	if @picture_visible
		picture_hide
	else
		picture_show
	end	
end

def picture_resume_after_rollback
	if @picture_visible
		picture_show
	else
		picture_hide
	end	
end


def picture_show
	return unless @picture_file #&& !@picture_visible
	picture_set_visible true
	@myclass.picture_show
end

def picture_hide
	return unless @picture_file #&& @picture_visible
	@myclass.picture_hide
	picture_set_visible false
end

def picture_set_visible(on)
	@picture_visible = on
	hsh = { :picture_visible => @picture_visible }
	registry_info_contribute(hsh)
end

#PICTURE: ask for a picture file via an Open panel
def picture_ask_file
	if @picture_file
		path = File.dirname @picture_file
	else	
		path = File.dirname @model.path.gsub(/\\/, '/')
	end	
	file = UI.openpanel T6[:T_TXT_SelectImageFile], path, "#{T6[:T_TXT_ImageFiles]}|*.jpg;*.png"
	return unless file
	@picture_file = file.gsub(/\\/, '/')
	hsh = { :picture_file => @picture_file }
	registry_info_contribute(hsh)
	picture_hide
	@cleanser.picture_match_stop
	@cleanser.picture_match_start
end

#PICTURE: Enter the match mode for picture
def picture_matching? ; @cleanser && @cleanser.picture_matching? ; end
def picture_match
	@myclass.picture_match_start
end

end	#class TopoShaperTool

end	#End Module F6_TopoShaper
