=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Designed February 2013 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__mixin.rb
# Original Date	:   18 Feb 2013
# Description	:   TopoShaper mixin modules for sharing methods
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_TopoShaper

T6[:TIT_CalculatingTerrain] = "Calculating Terrain"
T6[:TIT_UpdatingTerrain] = "Updating Terrain"
T6[:TIT_CleansingContours] = "Cleansing Contours"

T6[:VBAR_analyze_contours] = "Analyzing Contours"
T6[:VBAR_compute_hull] = "Computing the Enveloppe"
T6[:VBAR_generate_grid] = "Generating the Grid"
T6[:VBAR_sampling_grid] = "Sampling contours on the Grid"
T6[:VBAR_calculate_zones] = "Determining the Zones"
T6[:VBAR_method_multi] = "Determining nodes: Multiple"
T6[:VBAR_method_border] = "Determining Nodes: Border"
T6[:VBAR_method_single] = "Determining Nodes: Single"
T6[:VBAR_altitude_multi] = "Interpolating Altitudes"
T6[:VBAR_altitude_single] = "Extrapolating Altitudes"
T6[:VBAR_calculate_cell] = "Calculating Mesh"
T6[:VBAR_calculate_bounds] = "Calculating Boundaries"
T6[:VBAR_instantiate_bounds] = "Finalizing Boundaries"
T6[:VBAR_update_cell] = "Updating Mesh"
T6[:VBAR_calculate_wall] = "Calculating Skirt"
T6[:VBAR_prepare_camera] = "Calculating Views"
T6[:VBAR_prepare_dessin] = "Preparing Preview"
T6[:VBAR_update_dessin] = "Updating Preview"
T6[:VBAR_analyze_junctions] = "Analyzing Junctions"
T6[:VBAR_analyze_hooks] = "Analyzing small hooks"
T6[:VBAR_simplify_contours] = "Simplifying contours"

T6[:TIT_GeneratingGeometry] = "Generating the Terrain Surface"
T6[:VGBAR_FillMesh] = "Creating the Mesh"
T6[:VGBAR_GenMesh] = "Generating the surface"
T6[:VGBAR_MarkDiago] = "Marking the diagonals as quadmesh"
T6[:VGBAR_GenWall] = "Generating the wall"
T6[:VGBAR_GenContours] = "Generating the contours"
T6[:VGBAR_GenMap] = "Generating the contour map"

T6[:INFO_ContourEdges] = "Contours: %1 - Edges: %2"
T6[:INFO_Repairs] = "Junctions: %1 - Hooks: %2 - Simplif: %3"
T6[:INFO_CalculationTime] = "Calculation time: %1s"

T6[:VBAR_CreatingEdgeWire] = "Creating Edge wireframe"

#=============================================================================================
#=============================================================================================
# Mixin Nodule for common operations on cleansing and terrain generation
#=============================================================================================
#=============================================================================================

module TopoShaperContour_mixin

attr_reader :bb_extents

Topo_GenContour = Struct.new :inum, :icontour_3d, :pts_orig, :pts, :ptsxy, :bbxy, :loop, :hull, :area, :boxinfo,
							 :circles, :circles_3d, :lines, :lines_ds, :lines_xy, :lines_xy_ds, 
						     :zmin, :zmax, :zavg, :avg_alti, :normal, :loop, :iz_beg, :iz_end, :nseg, :length,
							 :selected, :ignored, :option_hilltop, :option_cliff, :single, :simplified, :simplifiable,
							 :associate_contours, :associate_repairs, :altitude_changed, :altitude

Topo_Dessin = Struct.new :grid_lines, :contour_lines, :contour_lines_situ, :contour_lines_xy, :arlequin0, :arlequin1,
                         :hull_lines, :surface_triangles, :surface_lines, :wall_quads, :wall_lines,
						 :in_situ, :done
						 							 
#-----------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------
# CONTOUR: Contour Management
#-----------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------

#CONTOUR: Initialize the environment for analysing contours
def contour_init_env
	@nseg_circle = 7
	@ndiv_boite = 6
	@tol = 0.1

	@tr_bello = Geom::Transformation.scaling(ORIGIN, 2)
	@tr_bello_inv = @tr_bello.inverse	
	
end

#CONTOUR: Analyse and create the contour information
def contour_analysis(lst_pts)
	#Initialization
	contour_init_env
	
	#Calculating transformation from the plane normal
	axes = @plane_normal.axes
	@tr_axe_plane_inv = Geom::Transformation.axes(ORIGIN, *axes).inverse

	#Registering the contours
	@lst_contours = []
	@lst_contours_loop = []
	@lst_contours_open = []
	n = lst_pts.length
	@total_nb_edges = 0
	lst_pts.each_with_index do |pts, ik| 
		vbar_mini_progression(n, ik, 100, 50)		
		@total_nb_edges += pts.length-1
		contour = Topo_GenContour.new
		contour.pts_orig = pts
		contour.pts = pts.collect { |pt| @tr_axe_plane_inv * pt }
		contour.ptsxy = contour.pts.collect { |pt| Geom::Point3d.new(pt.x, pt.y, 0) }
		contour.inum = ik
		if pts.first == pts.last
			contour.loop = true
			contour.area = G6.polygon_area contour.ptsxy
			@lst_contours_loop.push contour
		else	
			@lst_contours_open.push contour
		end	
		len = 0
		for i in 0..pts.length-2
			len += pts[i].distance pts[i+1]
		end
		contour.length = len
		@lst_contours.push contour
	end	
	
	#Compute the global boundaries for all contours
		#Calculating the fitting box in 2D
	ptsxy = []
	@lst_contours.each { |contour| ptsxy += contour.ptsxy }
	pts_hull = Traductor::ConvexHull2D.compute ptsxy
	box = Traductor::BestFit2d.best_fitting_box Z_AXIS, pts_hull
	
	#Normalizing the box orientation
	vec01 = box[0].vector_to(box[1])
	vec12 = box[1].vector_to(box[2])
	box = [box[0], box[3], box[2], box[1]] if (vec01 * vec12) % Z_AXIS < 0
	box = [box[2], box[3], box[0], box[1]] if box[0].x > box[2].x
	iref = (box[0].distance(box[1]) > box[1].distance(box[2])) ? 0 : 1
	vecx = box[iref].vector_to box[iref+1]
	vecy = box[iref+1].vector_to box[iref+2]

	#Calculating the transformations
	tr_rot = Geom::Transformation.axes(ORIGIN, vecx, vecy, Z_AXIS).inverse
	box_xy = box.collect { |pt| tr_rot * pt }
	box_xy = box_xy.sort { |a, b| (a.x == b.x) ? a.y <=> b.y : a.x <=> b.x }
	pt0 = box_xy[0]
	vec0 = pt0.vector_to ORIGIN
	tt = Geom::Transformation.translation vec0
	tr_rot = tt * tr_rot
	@tr_tot = tr_rot * @tr_axe_plane_inv
	@tr_tot_inv = @tr_tot.inverse
	
	#Orientating the contours
	box = box.collect { |pt| tr_rot * pt }	
	@lst_contours.each do |contour|
		contour.pts = contour.pts.collect { |pt| tr_rot * pt }
		contour.ptsxy = contour.ptsxy.collect { |pt| tr_rot * pt }	
	end
	@main_box = box

	#Calculating the global boundaries
	contour_global_boundaries
	
	#Loading the attributes of each contour if applicable
	attribute_load_contour_properties @group_attr
end

#CONTOUR: Compute the global boundaries for all contours
def contour_global_boundaries
	@altitude_reference = @tool.registry_info_get[:alt_ref] if @tool
	@altitude_reference = 0 unless @altitude_reference

	#Calculating the boundaries for model
	@zmin_ct = @zmax_ct = @lst_contours[0].pts[0].z
	@lst_contours.each do |contour|
		pts = contour.pts
		zmin = zmax = pts[0].z
		pts.each do |pt|
			z = pt.z
			zmin = z if z < zmin
			zmax = z if z > zmax
		end	
		contour.zmin = zmin
		contour.zmax = zmax
		@zmin_ct = zmin if zmin < @zmin_ct
		@zmax_ct = zmax if zmax > @zmax_ct
	end
	
	#Bounding box for all contours
	@bbxy = Geom::BoundingBox.new
	@bbxy.add @main_box
	@xmin = @bbxy.corner(0).x
	@ymin = @bbxy.corner(0).y
	@xmax = @bbxy.corner(1).x
	@ymax = @bbxy.corner(2).y
	@xyavg = (@xmax - @xmin + @ymax - @ymin) * 0.5
	@zmin = @zmin_ct
	@zmax = @zmax_ct
	@big_length = 2 * @xyavg

	#Calculating the transformation for dessin
	@tr_dessin = Geom::Transformation.translation Geom::Vector3d.new(1.1 * (@xmin - @xmax), 0, -@zmin_ct)
	@tr_dessin_inv = @tr_dessin.inverse
	
	#Calculating the bounding box for the Map
	@bb_map = Geom::BoundingBox.new.add @main_box.collect { |pt| @tr_bello * pt }
	
	#Bounding box extents
	@bb_extents = Geom::BoundingBox.new
	@bb_extents.add @main_box.collect { |pt| @tr_dessin * pt }
	@bb_extents.add @bb_map
	
	#Calculating the bounding box for each contour
	@hull_proximity = @xyavg * 0.02
	@lst_contours.each { |contour| contour_compute_boundaries(contour) }
	
	#Calculating the bounding boxes for the Surface
	ptmin = Geom::Point3d.new @xmin, @ymin, @zmin_ct
	ptmax = Geom::Point3d.new @xmax, @ymax, @zmax_ct
	@bb_3d = Geom::BoundingBox.new.add ptmin, ptmax
	
	@bb_surface = Geom::BoundingBox.new
	@bb_surface.add @tr_dessin * ptmin, @tr_dessin * ptmax
	@bb_surface_map = Geom::BoundingBox.new.add @bb_surface, @bb_map
	@bb_extents.add @bb_surface	
end

#CONTOUR: Compute the bounding box and the circle information for a contour
def contour_compute_boundaries(contour)
	pts = contour.pts
	n = contour.pts.length-1
	len = 0
	for i in 0..n-1
		len += pts[i].distance pts[i+1]
	end	
	zavg = 0
	contour.pts.each { |pt| zavg += pt.z }
	contour.zavg = zavg / (n+1)
	contour.altitude = contour.zavg + @altitude_reference
	contour.bbxy = contour_compute_bbxy(contour.ptsxy, extra=0)
	contour.circles = G6.contour_compute_proximity_circles(contour.ptsxy, @nseg_circle)
	contour.circles_3d = G6.contour_compute_proximity_circles(contour.pts, @nseg_circle)
	contour_compute_boxinfo contour
end

#CONTOUR: Compute the bounding box info for each contour
def contour_compute_bbxy(ptsxy, extra=0)
	bb = Geom::BoundingBox.new.add(ptsxy)
	extra = 0 unless extra
	v = X_AXIS + Y_AXIS
	bb.add bb.corner(0).offset(v, -extra)
	bb.add bb.corner(3).offset(v, extra)
	bb 
end

#CONTOUR: Check if a circle is cut by a segment
def circle_cut_by_segment?(pt1, pt2, circle)
	ptcenter, radius, = circle
	d = ptcenter.distance_to_line([pt1, pt2])
	(d < (radius - 0.001))
end

def bb_2_points(pt1, pt2)
	vec = pt1.vector_to pt2
	if vec.valid?
		vecp = vec * Z_AXIS
		bb = Geom::BoundingBox.new.add [pt1.offset(vec, -5), pt2.offset(vec, 5), pt1.offset(vecp, -5), pt2.offset(vecp, 5)]
	else
		vecp = X_AXIS + Y_AXIS
		bb = Geom::BoundingBox.new.add [pt1.offset(vecp, -5), pt1.offset(vecp, 5)]
	end
end

#CONTOUR: Check if a segment cuts any contour, except at an extremity
def contour_cut_segment?(pt1, pt2, contours=nil)
	seg = [pt1, pt2]
	bb = bb_2_points(pt1, pt2)
	contours = @lst_contours unless contours
	contours.each do |contour|
		next if contour.bbxy.intersect(bb).empty?####
		ptsxy = contour.ptsxy
		contour.circles.each do |circle|
			ptcenter, radius, pts, ibeg = circle
			d = ptcenter.distance_to_line([pt1, pt2])
			next unless (d < (radius - 0.001))
			for i in 0..pts.length-2
				pt = G6.intersect_segment_segment(seg, [pts[i], pts[i+1]])
				return [contour, pt] if pt && pt != pt1 && pt != pt2 && pt != ptsxy.first && pt != ptsxy.last
			end
		end
	end	
	false
end

#CONTOUR: Check if a segment cuts a contour in a list, including at an extremity
#Return the contour, ptxy and pt3d inteserction information
def contour_segment_intersection(pt1, pt2, contours=nil)
	seg = [pt1, pt2]
	#bb = Geom::BoundingBox.new.add [pt1, pt2]
	bb = bb_2_points(pt1, pt2)
	ls = []
	contours = @lst_contours unless contours
	contours.each do |contour|
		next if contour.bbxy.intersect(bb).empty?####
		ptsxy = contour.ptsxy
		contour.circles.each do |circle|
			ptcenter, radius, pts, ibeg = circle
			d = ptcenter.distance_to_line([pt1, pt2])
			for i in 0..pts.length-2
				ptxy = G6.intersect_segment_segment(seg, [pts[i], pts[i+1]])
				if ptxy
					pt3d = Geom.intersect_line_line [ptxy, Z_AXIS], [contour.pts[ibeg+i], contour.pts[ibeg+i+1]]
					ls.push [contour, ptxy, pt3d, pt1.distance(ptxy)] 
					next
				end	
			end
		end
	end	
	return nil if ls.empty?
	ls.sort! { |a, b| a[3] <=> b[3] }
	ls
end

#Toggle Selection of a contour
def contour_toggle_select(contour)
	contour.selected = !contour.selected
end

#Select or Unselect ALL contour
def contour_select_all(onoff)
	@lst_contours.each { |contour| contour.selected = onoff }
end
	
#Select or Unselect ALL contour
def contour_selection
	@lst_contours.find_all { |contour| contour.selected }
end

#METHOD: Calculate the 3d Point corresponding to a XY point on a contour
def contour_3d_from_2d(ptxy, contour)
	pts = contour.pts
	for i in 0..pts.length-2
		pt1 = pts[i]
		pt2 = pts[i+1]
		pt = Geom.intersect_line_line [pt1, pt2], [ptxy, Z_AXIS]
		return pt if pt
	end
	nil
end
	
#-----------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------
# VBAR: Visual Prrogress management
#-----------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------

#VBAR: Creating and starting the visual bar for calculation and updating
def vbar_start(action)
	#Creating the titles for each step
	case action
	when :full_processing
		lst = [:analyze_contours, :compute_hull, :generate_grid, :sampling_grid, :calculate_zones, 
			   :method_multi, :method_border, :method_single, :altitude_multi, :altitude_single, 
			   :calculate_bounds, :calculate_cell, :calculate_wall, :instantiate_bounds, :prepare_camera, :prepare_dessin]
		title = T6[:TIT_CalculatingTerrain]
	when :updating
		lst = [:altitude_multi, :altitude_single, :calculate_bounds, :update_cell, :calculate_wall, 
		       :instantiate_bounds, :update_dessin]
		title = T6[:TIT_UpdatingTerrain]
	when :cleansing
		lst = [:analyze_contours, :analyze_junctions, :analyze_hooks, :simplify_contours, :prepare_camera, :prepare_dessin]
		title = T6[:TIT_CleansingContours]
	end
	
	#Creating the vbar
	@vbar_step = 1.0 / lst.length	
	@hsh_vbar = {}	
	lst.each_with_index do |key, i|
		@hsh_vbar[key] = [T6["VBAR_#{key}".intern], i * @vbar_step]
	end	
	@vbar = Traductor::VisualProgressBar.new title, { :mini_delay => 0.4 }
	
	#Starting the vbar
	@vbar.start
end

def vbar_stop
	@time_calculation = @vbar.stop
end
	
def vbar_refresh(view)
	@vbar.draw view if @vbar
end

def vbar_progression(key)
	return unless @vbar
	msg, pc = @hsh_vbar[key]
	@vbar.progression pc, msg
end

def vbar_mini_progression(n, i, min, slice=100)
	return unless @vbar
	return if n < min || n < 2 * slice 
	@vbar.mini_progression(i * 1.0 / n, @vbar_step) if i.modulo(slice) == 0
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# CAMERA: Manage camera for working views
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

#CAMERA: Compute the 3 cameras for the working views
def camera_compute
	vbar_progression :prepare_camera
	fov = @view.camera.fov.degrees
	vpratio = 1.0 * @view.vpheight / @view.vpwidth
	@hsh_camera = {} unless @hsh_camera
	
	#Camera for surface and surface + map
	[[:surface, @bb_surface], [:surface_map, @bb_surface_map]].each do |a|
		symb, bb = a
		target = bb.center
		pt0 = bb.corner(2)
		pt1 = bb.corner(1)
		ptmid = Geom.linear_combination 0.5, pt0, 0.5, pt1
		vec = pt0.vector_to pt1
		w = bb.width
		h = bb.height
		d = Math.sqrt(w * w + h * h)
		d2 = 2 * w * Math.sin(Math.atan2(h, w))
		d = d2 if d2 > d
		pteye = ptmid.offset vec * Z_AXIS, 0.5 * d / Math.atan(fov)
		z = [(@zmax - @zmin) * 3, d * 0.5].max
		eye = Geom::Point3d.new pteye.x, pteye.y, z
		camera_create symb, eye, target, Z_AXIS	
	end
	
	#Camera for map
	dxmax = @bb_map.width
	dymax = @bb_map.height
	dmax = (dymax > dxmax * vpratio) ? dymax / vpratio : dxmax
	target = @bb_map.center
	eye = Geom::Point3d.new target.x, target.y, 0.5 * dmax / Math.atan(fov)
	camera_create :map, eye, target, Y_AXIS
	
	#Camera for geometry in situ
	cam_surface = @hsh_camera[:surface]
	t = @tr_tot_inv * @tr_dessin_inv
	#@hsh_camera[:geometry] = Sketchup::Camera.new t * cam_surface.eye, t * cam_surface.target, t * cam_surface.up
	@hsh_camera[:geometry] = Sketchup::Camera.new t * cam_surface.eye, t * cam_surface.target, t * Z_AXIS
end

def camera_create(symb, eye, target, up)
	camera = Sketchup::Camera.new eye, target, up
	camera.description = "TOPOSHAPER_#{symb}"
	@hsh_camera[symb] = camera
end

#CAMERA: Return a camera
def camera(symb)
	symb = :surface unless symb
	@hsh_camera[symb]
end

#VISU: Check if the current view is a working view
def camera_in_situ?
	(@view.camera.description !~ /TOPOSHAPER_/) ? true : false
end

def camera_puts(symb)
	camera = @hsh_camera[symb]
	puts "*****CAMERA #{symb.inspect} *********************"
	puts "target = #{camera.target}"
	puts "Direction = #{camera.direction} up = #{camera.up}"
	puts "Eye = #{camera.eye}"
	puts "Fov = #{camera.fov}"
	puts "**************************************************"
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# EDGEWIRE: generate fake edges for contours
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

#EDGEWIRE: Notify the change of view
def notify_working_view(view_state)
	case view_state
	when :in_situ
		edgewire_hide
	when :restore
		edgewire_erase
	else
		edgewire_show
	end	
end

#EDGEWIRE: Show construction lines for the contours
def edgewire_set_layer(layer)
	@edgewire_layer = layer
end

#EDGEWIRE: Show construction lines for the contours
def edgewire_show
	if @edgewire_group && @edgewire_group.valid? && @edgewire_group.entities.length > 0
		@edgewire_group.visible = true
		return
	end
	
	#Creating the group
	tlayer = @edgewire_layer
	t = @tr_dessin
	@edgewire_group = @model.active_entities.add_group
	@edgewire_group.layer = tlayer
	entities = @edgewire_group.entities
	
	#Creating the shadow elements in hard (to be visible when orbiting)
	if @myclass == :algo
		0.step(@wall_lines.length-2, 2) { |i| entities.add_cline t * @wall_lines[i], t * @wall_lines[i+1] }
		hullgpts = @hull_grid_pts.collect { |pt| pt2 = pt.clone ; pt2.z = @zmin ; t * pt2 }
		for i in 0..@hull_grid_pts.length-2
			entities.add_cline hullgpts[i], hullgpts[i+1]
		end	
		grid_lines = @dessin_info.grid_lines
		0.step(grid_lines.length-2, 2) { |i| entities.add_cline grid_lines[i], grid_lines[i+1] }
		
	elsif @myclass == :cleanser
		edgewire_create_face_plane
	end
	
	#Creating the shadow for the contours
	min = 100
	slice = 50
	nstep = @lst_contours.length
	hsh = { :delay => ((nstep > min) ? 0.0 : 0.3) }
	vgbar = Traductor::VisualProgressBar.new T6[:VBAR_CreatingEdgeWire], hsh
	vgbar.start
	@hsh_edgewire_cline = {}
	@hsh_edgewire_contour = {}
	@lst_contours.each_with_index do |contour, ik|
		vgbar.progression 1.0 * ik / nstep if ik > 0 && ik.modulo(slice) == 0
		icontour = contour.inum
		pts = contour.pts.collect { |pt| t * pt }
		for i in 0..pts.length-2
			cline = entities.add_cline pts[i], pts[i+1]
			@hsh_edgewire_cline[cline.entityID] = icontour
			llc = @hsh_edgewire_contour[icontour] 
			llc = @hsh_edgewire_contour[icontour] = [] unless llc
			llc.push cline
		end
	end	
	
	#Showing the picture if applicable
	picture_show if @myclass == :cleanser && @tool.picture_visible?
	
	vgbar.stop if vgbar
end

#EDGEWIRE: Create the plane face
def edgewire_create_face_plane
	pts = []
	pts.push Geom::Point3d.new(@xmin, @ymin, 0)
	pts.push Geom::Point3d.new(@xmax, @ymin, 0)
	pts.push Geom::Point3d.new(@xmax, @ymax, 0)
	pts.push Geom::Point3d.new(@xmin, @ymax, 0)
	face = @edgewire_group.entities.add_face pts.collect { |pt| @tr_bello * pt }
	face.material = face.back_material = @color_plane_edgewire_cleanser
	face.layer = @edgewire_layer
	@edgewire_plane_face = face
	edgewire_change_color_plane
end

#EDGEWIRE: Change the color of the 2D plane
def edgewire_change_color_plane
	return unless @edgewire_plane_face
	color = (@editing_altitude) ? @color_plane_edgewire_altitude : @color_plane_edgewire_cleanser	
	@edgewire_plane_face.material = @edgewire_plane_face.back_material = color
end

def edgewire_plane_face_show(on)
	if on
		edgewire_create_face_plane unless @edgewire_plane_face
	else
		@edgewire_plane_face.erase! if @edgewire_plane_face
		@edgewire_plane_face = nil
	end	
end

#EDGEWIRE: Hide the fake contours
def edgewire_hide
	@edgewire_group.visible = false if @edgewire_group && @edgewire_group.valid?
end

#EDGEWIRE: Remove the fake contours
def edgewire_erase
	@edgewire_group.erase! if @edgewire_group && @edgewire_group.valid?
	@edgewire_group = nil
	@edgewire_plane_face = nil
	@picture_su_image = nil
end

#EDGEWIRE: Move the cline when modifying the altitude of a contour
def edgewire_move_contour(contour, delta)
	tr = Geom::Transformation.translation(Geom::Vector3d.new(0, 0, delta))
	lcline = @hsh_edgewire_contour[contour.inum]
	@edgewire_group.entities.transform_entities tr, lcline
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# DESSIN: Drawing routine for GUI
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

def colors_init
	@on_black = !G6.su_capa_color_polygon
	@mouse_precision = 6
	#For SU versions after SU8 M1
	if @on_black
		@color_surface_working = 'black'
		@color_surface = 'black'	
		@color_contour_algo = 'lightgrey'
		@color_wall = 'black'
	else
		#@color_surface_working = 'lightgreen'
		@color_surface_working = MYDEFPARAM[:TPS_ColorTerrain]
		@color_surface = 'lightblue'
		#@color_contour_algo = 'black'
		@color_contour_algo = MYDEFPARAM[:TPS_ColorIso]
		@color_wall = 'khaki'
		@color_wall = MYDEFPARAM[:TPS_ColorSkirt]
	end
	@color_cell = 'yellow'
	@color_junction = 'red'
	@color_hook = 'magenta'
	@color_hull = 'purple'
	@color_geometry_map = 'moccasin'
	@color_sel_contour = 'blue'
	@color_alt_contour = 'orange'
	@color_hi_contour = Sketchup::Color.new 'gray'
	@color_hi_contour.alpha = 0.6
	@hsh_node_colors = { :multi => 'red', :on_contour => 'orange', :single => 'magenta', :out => 'salmon',
	                     nil => 'gray', :friend => 'yellow' }
						 
	@hsh_boxinfo_altitude = { :bk_color => 'lightgreen', :fr_color => 'red' }					 
	@hsh_boxinfo_extrem = { :bk_color => 'lightblue', :fr_color => 'blue' }					 
	@hsh_boxinfo_contour = { :bk_color => 'yellow', :fr_color => 'orange' }					 
	@hsh_boxinfo_junction_accept = { :bk_color => 'lightgreen', :fr_color => 'green' }					 
	@hsh_boxinfo_junction_ignore = { :bk_color => 'pink', :fr_color => 'red' }	
	@hsh_boxinfo_match_move = { :bk_color => 'lightblue', :fr_color => 'blue' }
	@hsh_boxinfo_match_adjust = { :bk_color => 'lightgreen', :fr_color => 'green' }
	
	@color_plane_edgewire_cleanser = 'palegoldenrod'
	@color_plane_edgewire_altitude = 'powderblue'
end

#DESSIN: Drawing contours
def dessin_single_contour(view, contours, type_color)
	return unless contours
	view.line_stipple = ''
	view.line_width = 3	
	view.drawing_color = @hsh_node_colors[type_color]
	t = (@dessin_info.in_situ) ? @tr_tot_inv : @tr_dessin
	contours.each do |contour|
		view.draw2d GL_LINE_STRIP, contour.ptsxy.collect { |pt| view.screen_coords(@tr_bello * pt) }
		view.draw2d GL_LINE_STRIP, contour.pts.collect { |pt| view.screen_coords(t * pt) }
	end
end

#DESSIN: Drawing contours
def dessin_hi_contour(view, contour=nil)
	contour = @hi_contour unless contour
	return unless contour
	view.line_stipple = (contour.ignored) ? '_' : ''
	view.line_width = 6
	view.drawing_color = @color_hi_contour
	t = (@dessin_info.in_situ) ? @tr_tot_inv : @tr_dessin
	view.draw2d GL_LINE_STRIP, contour.ptsxy.collect { |pt| view.screen_coords(@tr_bello *pt) }
	view.draw2d GL_LINE_STRIP, contour.pts.collect { |pt| view.screen_coords(t * pt) }
end

#DESSIN: Drawing contours
def dessin_selected_contours(view)
	view.line_stipple = ''
	view.line_width = 3	
	view.drawing_color = @color_sel_contour
	t = (@dessin_info.in_situ) ? @tr_tot_inv : @tr_dessin
	@lst_contours.each do |contour|
		next unless contour.selected
		view.draw GL_LINE_STRIP, contour.ptsxy.collect { |pt| G6.small_offset(view, @tr_bello *pt, 2) }
		view.draw2d GL_LINE_STRIP, contour.pts.collect { |pt| view.screen_coords(t * pt) }
	end	
end

#-------------------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------------------
# MOUSE: Some common method for Manage mouse location and click
#-------------------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------------------

#MOUSE: Find the closest contour in the map
def mouse_contour_in_map(pt)
	size = @view.pixels_to_model @mouse_precision, pt
	ptxy = @tr_bello_inv * Geom::Point3d.new(pt.x, pt.y, 0)
	
	hi_contour = nil
	dmin = size
	@lst_contours.each do |contour|
		d, iseg, pt, ptc = G6.proximity_point_curve_by_circle(ptxy, contour.circles, dmin)
		if d && d < dmin
			hi_contour = contour
			dmin = d
		end
	end
	hi_contour
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# ATTR: Write and read attributes for Group
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

@@dico = "TOPOSHAPER"
@@dico_general = "General"
@@dico_grid = "Grid"
@@dico_hull = "Hull"
@@dico_plane_normal = "PlaneNormal"

#ATTR: Storing Terrain parameters as attribute of the group
def attribute_write(group)
	trg = @tr_tot_inv

	#General information
	hsh = { :nb_contours => @lst_contours.length, :hull_mode => @hull_mode }
	group.set_attribute @@dico, @@dico_general, hsh.inspect.gsub('"', "'")
	group.set_attribute @@dico, @@dico_plane_normal, @plane_normal.to_a
	
	#Grid
	hsh = { :nx => @nx, :ny => @ny }
	group.set_attribute @@dico, @@dico_grid, hsh.inspect
	
	#Hull
	group.set_attribute @@dico, @@dico_hull, @hull_pts.collect { |pt| (trg * pt).to_a }
	
	#Store the contours
	@lst_contours.each_with_index do |contour, ik|
		group.set_attribute @@dico, "Contour_#{ik}", contour.pts.collect { |pt| (trg * pt).to_a }
	end	

	#Store the properties of each contours
	@lst_contours.each_with_index do |contour, ik|
		hsh = {}
		ht = contour.option_hilltop
		hsh[:option_hilltop] = ht if ht
		group.set_attribute @@dico, "KParam_#{ik}", hsh.inspect
	end	

end

#ATTR: Loading Terrain parameters as attribute of the group
def attribute_load(group)
	return unless group
	
	t0 = Time.now
	
	#General information
	sparam = group.get_attribute @@dico, @@dico_general
	return true unless sparam && sparam.strip =~ /\A\{.+=>.+\}\Z/
	begin
		hsh = eval sparam
	rescue
		return true
	end	
	nb_contours = hsh[:nb_contours]
	@hull_mode = hsh[:hull_mode]
	
	#Plane_normal
	vvec = group.get_attribute @@dico, @@dico_plane_normal
	@plane_normal = (vvec) ? @tr_attr * Geom::Vector3d.new(*vvec) : Z_AXIS
	
	#Grid
	hsh = { :nx => @nx, :ny => @ny }
	sparam = group.get_attribute @@dico, @@dico_grid
	begin
		hsh = eval(sparam) if sparam
		@nx = hsh[:nx]
		@ny = hsh[:ny]
	rescue		
	end	
		
	#Load the contours
	begin
		@lst_pts = []
		for ik in 0..nb_contours-1
			pts = group.get_attribute @@dico, "Contour_#{ik}"
			@lst_pts.push pts.collect { |pt| @tr_attr * Geom::Point3d.new(*pt) }
		end	
	rescue
		return true
	end
	
	false
end

#ATTR: Loading the hull contour
def attribute_load_hull(group)
	return nil unless group
	t = @tr_tot * @tr_attr
	hull_pts = group.get_attribute @@dico, @@dico_hull
	return nil unless hull_pts
	hull_pts = hull_pts.collect { |pt| t * Geom::Point3d.new(*pt) }
	hull_pts.each { |pt| pt.z = 0 }
	hull_pts
end

#ATTR: Loading the properties of the contours
def attribute_load_contour_properties(group)
	return nil unless group
	for ik in 0..@lst_contours.length-1
		sparam = group.get_attribute @@dico, "KParam_#{ik}"
		next unless sparam
		hsh = eval sparam
		contour = @lst_contours[ik]
		contour.option_hilltop = hsh[:option_hilltop]
	end	
end

#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# UTILITY: Some utility
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------

#Transfer values from array <b> into array <a> with same index and with application of a procedures
#Faster than the native Ruby Array[ibeg, len]
def array_transfer (a, b, ibeg, len, &proc)
	for i in ibeg..ibeg+len-1
		a[i] = proc.call(b[i])
	end	
end

#Transfer values from array <b> into array <a> with index of b starting at 0
#Faster than the native Ruby Array[ibeg, len]
def array_assign (a, b, ibeg, len)
	for i in ibeg..ibeg+len-1
		a[i] = b[i-ibeg]
	end	
end
				
end	#module TopoShaperContour_mixin

end	#End Module F6_TopoShaper
