## Calculate basic "vector" functions using triplets and/or point name. 
## Return empty string if not able to calculate result.
## Next table shows function (op) followed by one or more args (p1, p2, p3 or const),
##   where p1, p2, p3 are triplets or point names const is integer or float scalar.  
## <table border>
## <tr><td><b>nrm p1               </b></td><td> normalized vector </td></tr>
## <tr><td><b>dir p1 p2            </b></td><td> normalized vector from p1 to p2 </td></tr>
## <tr><td><b>len p1               </b></td><td> length of vector</td></tr>
## <tr><td><b>distance p1 p2       </b></td><td> distance between p1 and p2</td></tr>
## <tr><td><b>sum p1 p2            </b></td><td> vector sum</td></tr>
## <tr><td><b>diff p1 p2           </b></td><td> vector diff</td></tr>
## <tr><td><b>smult p2 const       </b></td><td> vector scaled</td></tr>
## <tr><td><b>sum_smult p1 p2 const</b></td><td> vector sum scaled</td></tr>
## <tr><td><b>dot p1 p2            </b></td><td> dot product p1*p2</td></tr>
## <tr><td><b>mult p1 p2           </b></td><td> cross product p1Xp2</td></tr>
## <tr><td><b>product p1 p2        </b></td><td> cross product p1Xp2</td></tr>
## <tr><td><b>angle p1 p2 p3       </b></td><td> angle between p1-p2 and p1-p3 lines</td></tr>
## <tr><td><b>angle p1 p2          </b></td><td> angle between 000-p1 and 000-p2</td></tr>
## <tr><td><b>vec_norm p1          </b></td><td> some vector normal to p1 </td></tr>
## </table>


proc ic_vcalc {{op ""} args} {
	if {$op == ""} {
		ic_mess "Options  args: \n\
		nrm p1; dir p1 p2; len p1; distance p1 p2; sum p1 p2; diff p1 p2;\n\
		smult p1 c; sum_smult p1 p2 c; nrm_smult p1 c;\n\
		dot p1 p2; mult p1 p2;\n\
		product p1 p2; product p1 p2 p3;angle p1 p2; angle p1 p2 p3;\n\
		vec_norm p1 center p1 p2 p3\n"
		return
	}
  if {$args == ""} return
# support ViPoints 
# 03/14/07 : support med points with weird names (such as a+b, b-c, ...)
foreach v $args {
  if {[lindex [ic_vset -med_entity $v] 0] == "point"} {
    lappend newargs [ic_geo_get_point_location $v]
  } else {
    lappend newargs $v
  }
 }
 set args $newargs
# 
	set def ""
  switch $op {
    nrm   { 
	  if {[llength [lindex $args 0]] == 1} {
		set args [list [ic_vset -vec - $args]]
		}		
      if [ic_v_calc vector direction [eval list \{0 0 0\} $args] 1] return
    }
     len   {
			  if {[llength [lindex $args 0]] == 1} {
		set args [list [ic_vset -vec - $args]]
		}	
      if [ic_v_calc value distance [eval list \{0 0 0\} $args] 1] return
    }  
    dir   {
		foreach p $args {
			lappend def [ic_vset -vec - $p]
		}
		if [ic_v_calc vector direction $def 1] return
    }
 
    distance {
    		foreach p $args {
			lappend def [ic_vset -vec - $p]
		}
      if [ic_v_calc value distance $def 1] return
    }
    sum   {
    		foreach p $args {
			lappend def [ic_vset -vec - $p]
		}
      if [ic_v_calc vector sum $def 1] return
    }
    diff  {
    		foreach p $args {
			lappend def [ic_vset -vec - $p]
		}
      if [ic_v_calc vector diff $def 1] return
    }
    smult  {
		foreach "p c" $args ""
		set p [ic_vset -vec - $p]
		set c [ic_vset - $c] 
      if [ic_v_calc vector sum_scale [list "0 0 0" $p $c] 1] return
    }
	sum_smult {
		foreach "p1 p2 c" $args ""
		set p1 [ic_vset -vec - $p1]
		set p2 [ic_vset -vec - $p2]		
		set c [ic_vset - $c] 
      if [ic_v_calc vector sum_scale [list $p1 $p2 $c] 1] return
    }
	nrm_smult {
		foreach "p c" $args ""
		set p [ic_vset -vec - $p]	
		set c [ic_vset - $c]
		if {$c == ""} return 
		if [ic_v_calc vector direction [list "0 0 0" $p] 1] return
		set p [lindex [ic_virtual] 0]
		if [ic_v_calc vector sum_scale [list "0 0 0" $p $c] 1] return 
	}
    dot   {
    		foreach p $args {
			lappend def [ic_vset -vec - $p]
		}
      return [eval ic_geo_vec_dot $def]
    }
    mult - product  {
		# product using 3 points instead of 2 vectors:
		foreach n {0 1 2} {
			set p$n [ic_vset -vec - [lindex $args $n]]
		}
		if {$p2 == ""} {
			lappend def $p0 $p1
		} else {
			lappend def [ic_vcalc diff $p0 $p1]
			lappend def [ic_vcalc diff $p0 $p2]
		}				
      if [ic_v_calc vector product $def 1] return
    }
    angle {
		foreach n {0 1 2} {
			set p$n [ic_vset -vec - [lindex $args $n]]
		}
		if {$p2 == ""} {
			lappend def $p0 $p1
		} else {
			lappend def [ic_vcalc diff $p0 $p1]
			lappend def [ic_vcalc diff $p0 $p2]
		}
		foreach n $def {
			if {$n == "" || ![ic_vcalc len $n]} return
		} 		
      if [ic_v_calc value angle $def 1] return
    }
	angle_normal {
	# format: axis vector1 vector2
	# angle in plane normal to axis from vector1 to vector2
	# using temprary local csys: n
	# axis must be different from vector1
		foreach "ax v1 v2" $args ""
		set ax2 [ic_vcalc product $ax $v1]
		set ax1 [ic_vcalc product $ax $ax2]
		if {$ax2 == "" || ($ax2 == "0 0 0")} return
		foreach v "ax ax1 ax2" {set $v [ic_vcalc nrm [set $v]]} 
		ic_csystem_create tmpcsys cylindrical "0 0 0" $ax1 $ax2 $ax
		set was_csys [ic_csystem_get_current]
		ic_csystem_set_current tmpcsys
		set rtz1 [ic_coords_into_local $v1]		 
		set rtz2 [ic_coords_into_local $v2]
		ic_csystem_set_current $was_csys 
		ic_csystem_delete tmpcsys
		set res [expr [lindex $rtz1 1]-([lindex $rtz2 1])]
		return $res
	}
    vec_norm {
        if [ic_v_calc vector product [eval list \{1 0 0\} $args] 1] return
        set nvec [ic_virtual]
        set len  [ic_v_calc value distance [eval list \{0 0 0\} $nvec]]
        if !$len {
            set nvec [ic_v_calc vector product [eval list \{0 1 0\} $args]]
            set len  [ic_v_calc value distance [eval list \{0 0 0\} $nvec]]
            if !$len {return {0 0 1}}
        }
        ic_v_calc vector direction [eval list \{0 0 0\} $nvec]
    }
	center {
       if {[llength $args] != 3} return
	   foreach i {0 1 2} {
			set p [ic_vset -vec - [lindex $args $i]]
			if ![llength $p] return
			set p$i $p
		}
		set ang [ic_vcalc angle $p0 $p1 $p2]
		if {$ang < 1.0 || $ang > 179.0} return
		ic_virtual define point _V0 $p0 1
		ic_virtual define point _V1 $p1 1
		ic_virtual define point _V2 $p2 1
		ic_virtual define point _V "_V0 _V1 _V2 radius 0 angle 0 offset 0" 1
		if ![ic_virtual check point _V 1] {
			return [lindex [ic_virtual] 0]
		}
		return 
	}
    default { return "Invalid operation $op" }
  }
  return [lindex [ic_virtual] 0]
}
## Temporary change color and/or "width" of points, curves, surfaces
## <ul>
## <li> <b>color</b> white <i>or</i> red <i>or</i> green. Default color:
## family color.
## <li> <b>width</b> 1 <i>or</i> 2 <i>or</i> 3. Default width: 1.
## <li> <b>names</b> list of names. If list is empty, default settings
## will be restored
## </ul>

proc ic_highlight {args} {
    global geo_highlight tdv_default projlib
    # Modified 03/18/05 
	# a)to highlight more types; 
	# b) restore old colors
	# c) option add <names> to add
	# d) allow to use type instead of "names" keyword
	#
	foreach {width names type add color bsp_names solid unset} {2 "" "" "" white "" 0 ""} ""
#    set color [$tdv_default highlight_color]	
	set types [ic_geo_get_types]
    foreach {a v} $args {set $a $v}
    
    if {$unset != ""} {
    # unhighlight only this name. For preview
  		if [array exists geo_highlight] {
		set bsp_names [array names geo_highlight]
		foreach bsp $bsp_names {
		  # object may be deleted after last highlight
		  if [catch "$bsp get -name" obname] {
		    unset geo_highlight($bsp)
		    continue
		  }
			if {$obname != $v} continue
			set old $geo_highlight($bsp)
			if {[llength $old] > 1} {
				catch "eval $bsp configure $old"
				catch "update_geom_visible_obj $bsp"
				break
			}
		}
		}
		return
	}	 	 
    if {$add == ""} {
	if [array exists geo_highlight] {
	set bsp_names [array names geo_highlight]
	foreach bsp $bsp_names {
			set old $geo_highlight($bsp)
			if {[llength $old] > 1} {
				catch "eval $bsp configure $old"
				catch "update_geom_visible_obj $bsp"
				continue
			}
	}
	unset geo_highlight
	}
	if ![llength $args] {
		return $bsp_names
	}
	if {$names == ""} {
		foreach type $types {
			if [info exists $type] {
				set names [set $type]
				break
			}
		}
	} 
	} else {set names $add}
	if {$type != ""} {
		set types $type
	} 
    foreach name $names {
        set bsp ""
	foreach type $types {
            if {[ic_geo_check_objects_exist $type $name] == ""} continue
	    set bsp [$projlib get_object_by_name $type $name]
	    if {$bsp != ""} break
	}
	if {$bsp == ""} continue
	set set_opts ""
	set opts ""
	lappend opts -width [lindex [$bsp configure -width] end]
	lappend set_opts -width $width
	# some interference with geometry selection which set #ffffff  
	set was_col [lindex [$bsp configure -color] end]
	if {$color == ""} {
		# try highlight existing color
		set nrgb [scan $was_col "#%2s%2s%2s" r g b]
		if {$nrgb == 3} {
			set col "#"
			foreach c "$r $g $b" {
				set dc [expr 255-(255-[format %d 0x$c])*2/3]
				append col [format %x $dc]
			}
		} else {
			set col $was_col
		}
	} else {
	    set col $color
	}
	if {$was_col != "#ffffff"} {
	lappend opts -color $was_col 
	lappend set_opts -color $col 
	}
	lappend opts -visible [lindex [$bsp configure -visible] end]
	lappend set_opts -visible 1
	if {$solid != "0"} {
		lappend opts -shade [lindex [$bsp configure -shade] end]
		lappend set_opts -shade solid
		lappend opts -alpha [lindex [$bsp configure -alpha] end]
		lappend set_opts -alpha $solid
	}
	if {$type == "curve" && [$bsp get_object_type] == "mesh"} {
		lappend opts -draw_nodes 0
		lappend set_opts -draw_nodes 1
	}	 
	set geo_highlight($bsp) $opts
	catch "eval $bsp configure $set_opts" 
    }
}
set dont_record_command(ic_highlight) 1



## ic_vset options/parameters : return
## 		
## ic_vset	-names			:   all names* 
## ic_vset	-names type :		names of that type
## ic_vset	-method			:   all methods
## ic_vset	-method	type : methods for that type
## ic_vset	-method type method :	format of the method of the type 
## ic_vset	-method	name :	method for that name (if not default)
## ic_vset	-database		:	all names of databases with items number
## ic_vset 	-database	dname	:	make selected database current
## ic_vset	name			  : empty line if not defined**, or value (if any)
## ic_vset	name	def	  :	for variable set new definition
## ic_vset	-type	name	:	type of entity
## ic_vset	-def	name	:	definition of entity
## ic_vset	-info	name	:	detailed info on entity
## ic_vset 	-settings	debug	 : return	current debug level
## ic_vset 	-settings	debug	value	: set current debug level
## ic_vset 	-settings	med_pts		: current med points usage option 
## ic_vset 	-settings	med_pts	value	: set current med points usage option
## ic_vset	-settings	interrupt	value	: set 0/1 interrupt design creation (ic_vcreate)
## ic_vset	-vec	-	vec.expr.	: calculate �anonymous� vector expression
## ic_vset	-con	-	con.expr.	: calculate �anonymous� constraint expression
				
## ic_vset	-	expression :	calculate expression without database modification				
## ic_vset 	-delete	name :		delete entity
## ic_vset 	-delete	all	:	  delete all entities in active database
## ic_vset  : (without parameters) return last result or last reason for error**
## *  options may be abbreviated to 3 or more characters: -nam, -met, -dat, -set�
## ** most commands return empty line on invalid input
proc ic_vset {args} {
    if ![ic_geo_is_loaded] ic_empty_tetin
	return [eval vic_vset $args]
}
proc ic_vsettings {args} {
# This command is a copy of ic_vset, but will be recorded.
# Some settings (e.g. radians-degree switch for Comak) 
# would be useful for replay file.
	return [eval vic_vset $args]
}

proc ic_vdefine {args} {
## ic_vdefine	name	type	method_name*	definition
	#mod 11/23/05: return value if available 
	eval vic_vdefine $args
	return [ic_vset [lindex $args 0]]	
}
proc ic_vfile {args} {
## ic_vfile	read	filename	: read Vid file into current database
## ic_vfile	write	filename	: write current database into Vid file


	return [eval vic_vfile $args]	
}
proc ic_vcreate {args} {
# ic_vcreate	replay_mode	Create entities in order of definitions*
# ic_vcreate	name	Create entity
	return [eval vic_vcreate $args]	
}
# ic_vset is mainly for info. Do not record!
# in order to record new definition use ic_vdefine
set dont_record_command(ic_vset) 1
# Record??? set dont_record_command(ic_vdefine) 1
# Record??? set dont_record_command(ic_vcreate) 1
# Record??? set dont_record_command(ic_vfile) 1
#
# New geo create functions (for comak interpretor)
# It's a combination of existing ic_ functions with a common format.
# 
# For 11.0:  
# ic_point, ic_curve, ic_surface, ic_image  
# are useful not only for comak stuff and must be available for user
# they (as well as ic_vcalc, ic_vset) have next features: 
# - always validate parameters; never crash.
# - have standart input/output format
# - accept variables, vectors and scalar/vector expressions
# - can be run in batch-mode, but have interface with GUI interpreter
#   using commands: status_message, ic_set_gui, vic_eval
#===============================
proc ic_point {method part name def args} {
# 11/30/05 set tol as geo_cad(toler) 
global projlib batch_mode
	set this ic_point 
    if ![regexp {[a-zA-Z]} [string index $name 0]] {
        set name P$name
    }
    if {$def == ""} return
# 
# method : one of 
# part : family name
# name : point name; 
# if name == "" return location in LCS, nothing will be created, part will be ignored.
# def  : definition, format is method-dependent
# args :
#  
	set tol [ic_gui_set geo_cad(toler)]
	set vid_mode [ic_gui_set vid_options(vid_mode)]
	set auxiliary [ic_gui_set vid_options(auxiliary)]
	set replace [ic_gui_set vid_options(replace)]
	if {$tol == ""} {set tol 0.001}

# args may overwrite above settings
# args usage: ic_point "" AA p1 "x+1,3,5" replace 1 vid_mode 1
    foreach {n v} $args { set $n $v }
#
#********************************************************
# Delete old entity with the same name or create new name?    
    if $replace {
		ic_delete_geometry point names $name 0
		ic_image delete $name
	} else {
		if !$batch_mode { set name [ic_vset -new_name $name all] }
	}
#******************************************************
#
# LCS usage: 
# if def includes point name [as location] - keep this location
# any triplet, vector or scalar expression - convert into LCS          
#
	set xyz ""
	set in_lcs 0
	set check_multiple 0
	set med_pnt_done 0
	set format unknown
	
    update idletasks
	switch $method {
	intersect {
		set format "curve surface/curve how" 
		# ??? how = first ??? second ??? ?
		if {[llength $def] < 2} {
			status_message "Data error" red
			return
		}
		foreach "crv something how" $def ""
		set something_type [string trim [ic_vset -med_entity $something]]
		if [vid_exist "$this $method" curve $crv] return 
		if {$something_type == "surface"} {
			set xyz [comak_intersect_curve_surface $part $name $crv $something]
		} elseif {$something_type == "curve"} {
			set curvenums curve:[$projlib get_number curve $crv]
			lappend curvenums curve:[$projlib get_number curve $something]
			set xyz [$projlib intersect_curves $curvenums $tol]
			set check_multiple 1
		} else return 
	}
	arc_center {
	   # only curve name
	   set def [lindex $def 0]
	   if {[ic_geo_check_objects_exist curve $def] != $def} return
	   if ![ic_geo_crv_is_arc $def] {
	      status_message "Curve $def is not an arc" red
        return
     }
     # may be circle...
     	set p1 [lindex [ic_geo_get_crv_pos 0.1 $def] 0]
     	set p2 [lindex [ic_geo_get_crv_pos 0.45 $def] 0]
     	set p3 [lindex [ic_geo_get_crv_pos 0.85 $def] 0]
     	set xyz [ic_vcalc center $p1 $p2 $p3]     	
	}
	center {
		set res [va_set_aux_pnts $part ${name}.00 $def $auxiliary $vid_mode]
		if {$res == ""} return
		# res is in global
		set xyz [eval ic_vcalc center $res]
	}
	on_surface {
		set format "srf u v"
		foreach {srf u v} $def ""
		if [vid_exist "$this $method" surface $srf] return
		set xyz [lindex [ic_geo_get_srf_pos $u $v $srf] 0]
	}
	surface_normal {
		set format "srf pnt dir"
		foreach {srf pnt dir} $def ""
		set xyz [ic_vset -vec - $pnt]
		if {$xyz == ""} return
		if [ic_geo_check_part $srf] {
			set srfs [ic_geo_get_objects surface $srf]
			set srf [ic_geo_nearest_surface_list $xyz $srfs]
		}
		foreach "u v" [ic_geo_find_nearest_srf_pnt $srf $xyz] ""
		set vec [lindex [ic_geo_get_srf_nrm $u $v $srf] 0]
		set angle [ic_vcalc angle $vec $dir]
		if {$angle > 90} {
			set vec [ic_vcalc smult $vec -1]
		}
		set xyz $vec
	}
	point_curve_tangent {
		set format "crv pnt dir"
		foreach {crv pnt dir} $def ""
		set par [ic_geo_find_nearest_crv_pnt $crv $pnt]
		set vec [ic_geo_get_crv_tan $par $crv]
		set angle [ic_vcalc angle $vec $dir]
		if {$angle > 90} {
			set vec [ic_vcalc smult $vec -1]
		}
		set xyz $vec			
	}
	copymove {
		set from [lindex $def 0]
		if {[ic_geo_check_objects_exist point $from] == ""} {
			ic_geo_cre_pnt $part $from [ic_vset $from]
		}
		set newc [ic_geo_duplicate point $from]
		ic_geo_set_name point $newc $name
		ic_geo_set_family point $part names $name 0	
		eval ic_move_geometry point names $name [lrange $def 1 end]
		set xyz [ic_geo_get_point_location $name]
		set med_pnt_done 1			 
	} 
	
	curve_end  {
	set format "curve how"
		foreach "crv how" $def ""
		if {$how == "both"} {
			set xyz [ic_geo_get_crv_pos 0 $crv]
			lappend xyz [lindex [ic_geo_get_crv_pos 1 $crv] 0]
			set check_multiple 1
		} else {			
			set xyz [ic_geo_get_crv_end_generic $crv $how]
			if ![llength $xyz] {
				status_message "Error creating curve end $crv $how" red
				return ""
			}
		}
	}
	crvend_par {
	set format "curve how par"
	# Expect how: one of xmax||xmin||ymax...||zmin
		foreach "crv how par" $def ""
		# par may be scalar expression
		set par [ic_vset - $par]
		if {$par == ""} return
		if {$par > 1.0} {set par 1.0}
		if {$par < 0.0} {set par 0.0}
		set xyz [ic_geo_get_crv_end_generic $crv $how]
		set endpar [ic_geo_find_nearest_crv_pnt $crv $xyz]
		if {$endpar > 0.5} {
			set par [expr 1-$par]
		}
		set xyz [lindex [ic_geo_get_crv_pos $par $crv] 0]
	}
	crv_par {
		foreach "crv par" $def ""
		set xyz [lindex [ic_geo_get_crv_pos $par $crv] 0]
	}
	projcurv {
	# format "point curve(s)" or "point family" 
		set from [lindex $def 0]
		set crv [lindex $def 1]
		if [ic_geo_check_family $crv] {
			set crv [ic_geo_get_objects curve $crv]
		}			
		set xyz [ic_geo_project_point curve $crv $from "0 0 0" 0]
	}				
	projsurf {
	# format "point surface(s)" or "point family"
	# 03/23/06 optional format "point family"
		set from [lindex $def 0]
		set srf [lindex $def 1]
		if [ic_geo_check_family $srf] {
			set srf [ic_geo_get_objects surface $srf]
		}	
		set xyz [ic_geo_project_point surface $srf $from "0 0 0" 0]
	}					
	default {
		# 
		set in_lcs 1
		set method default		 
		set xyz [ic_vset -vec - $def]
		if ![llength $xyz] {
			status_message [ic_vset]
			return
		}
		# xyz is in Global csys always. It may be from point name or xyz
		# Expression or other default definitions are in LCS
		# Note, that in vid mode default ic_point method is similar to ic_vdefine name vector... 
		# Difference:
		#  ic_point will NOT define vector if currently it's not possible to calculate xyz
		#  ic_vdefine will define vector (unless there are no syntax errors).
		# 
	}					
	}
	if {$xyz == ""} {
		status_message "Error creating $method point" red 
		return
	}
	if $med_pnt_done {
		return $name
	}
	if $check_multiple {
		set xyzs $xyz 
	} else {
		set xyzs [list $xyz]
	}
	set nnames ""

	foreach xyz $xyzs {	
		if $vid_mode {
			if {$method == "default"} {
				ic_vdefine $name vector $def
				status_message "vector $name defined: $def"
			} else {	
				ic_vdefine $name vector command $def
#				ic_vset $name $xyz
#				status_message "vector $name ($method) ($def) ($xyz)" 			
			}
			ic_image vidpoint_im $name $xyz		
		} else {
			set name [ic_geo_cre_pnt $part $name $xyz $in_lcs]
#			status_message "point $name $xyz"
		}
		lappend nnames $name
		# next line for multiple points
		set name [ic_vset -new_name $name all]
	}	
	return $nnames
}

#============================

## Create a bspline arc 
## From a center point and two points
##     ic_curve <b>arc_ctr_rad</b> PART_NAME NEW_CURVE_NAME {CENTER_POINT POINT_1 POINT_2 0.0 "" "" 0}
## From a center point and two points and radius
##     ic_curve <b>arc_ctr_rad</b> PART_NAME NEW_CURVE_NAME {CENTER_POINT POINT_1 POINT_2 RADIUS "" "" 0}
## Note: in case of a radius of 0.0, the arc radius will be calculated from the distance between CENTER_POINT and POINT_1
## From start/end points  
##     ic_curve <b>arc_ctr_rad</b> PART_NAME NEW_CURVE_NAME {POINT_1 POINT_2 POINT_3 0.0 "" "" 1}
## From start/end points and radius 
##     ic_curve <b>arc_ctr_rad</b> PART_NAME NEW_CURVE_NAME {POINT_1 POINT_2 POINT_3 RADIUS "" "" 1}


proc ic_curve {method part name def args} {
# Modified:
# 06/25/08 added circle from 2 to any points
#          replace as argument fixed

#mess "DB $method $part $name \"$def\" $args\n" red
# validate def for some methods: cannot be empty element as "{} {} {1 2 3}"
	if ![llength $method] {set method point}
	if {[lsearch "point arc default point" $method] >= 0} {
		foreach word $def {if ![llength $word] return}
	}
set this ic_curve
global projlib
# 
# method : one of arc,va_arc,copymove,trim_side, trim_middle
# part : family name
# name : curve name
# def  : definition, format is method-dependent
# args optional: tol cre_pnts
# 
# tolerance & same_pnt_tol 
	set tol [ic_gui_set geo_cad(toler)]
	if {$tol == 0} {set tol 0.001}
	set same_pnt_tol [ic_gui_set vid_options(same_pnt_tol)]	
	if {$same_pnt_tol == 0} {set same_pnt_tol 0.001}	
		
	# using triangulation tolerance for automatic reapproximation
	set tritol 0.001
	foreach {n v} [ic_set_meshing_params global 0] {
		if {$n == "gttol"} {set tritol $v}
	}
	
	set cre_pnts 0
#
	set vid_mode [ic_gui_set vid_options(vid_mode)]
	set auxiliary [ic_gui_set vid_options(auxiliary)]
	set replace [ic_gui_set vid_options(replace)]
#
#********************************************************

# name first character: add C if not valid
if ![regexp {[a-zA-Z]} [string index $name 0]] {
   set name C$name
}
 foreach {n v} $args {set $n $v}
 # args may include "...replace 1..." 
 # Delete old entity with the same name or create new name?    
     if {$replace || $vid_mode} {
		ic_delete_geometry curve names $name 0
		ic_image delete $name
	} else {
		set name [ic_vset -new_name $name all]
	}
	update idletasks
#
 
 # _____________________
	switch $method {
	extend_trim {
		# format: crvs target_crv [ends] [tan_ext]
		# end is 0 or 1 or "" for nearest (to crv2) end
		foreach "crvs target_crv ends tan_ext" $def ""
		if {![llength [eval ic_geo_check_objects_exist curve $crvs]] || \
		![llength [ic_geo_check_objects_exist curve $target_crv]]} {
			status_message "Select curves" red
			return
		}
		if {$tan_ext != 1} {set tan_ext 0}
		set res ""
		set ncrvs [llength $crvs]
		for {set i 0} {$i < $ncrvs} {incr i} {
			set crv [lindex $crvs $i]
			set end [lindex $ends $i]
			if {$end != "1" && $end != "0"} {set end ""} 
			lappend res [vid_extend_trim_curves $crv $target_crv $end $tan_ext]
		}
		return $res
	}
	contour {
# check with D regarding $how 
		foreach "srf how how1" $def ""
		set res ""
# srf may be a family name: use first (hope only) surface
		if [ic_geo_check_family $srf] {
			set srf [lindex [ic_geo_get_objects surface $srf] 0]
			if {$srf == ""} return
		}
		set crvs [ic_geo_create_surface_edges $srf]
		set new_list [comak_reorder_contour_crvs $how $how1 $crvs]
		# change names: first name: $how [xmin]  
		for {set i 0} {$i < [llength $new_list]} {incr i} {
			set oldn [lindex $new_list $i]
			set newn [lindex $name $i]
			lappend res [ic_geo_set_name curve $oldn $newn]
		}
		ic_geo_set_family curve $part names $res 0	
		return $res
	}
	project {
	#format:crv srf .......junk
		set crv [lindex $def 0]
		set srf [lindex $def 1]	
		if [ic_geo_check_part $srf] {
			set srf [ic_geo_get_objects surface $srf]
		}	 
		return [ic_geo_project_curve_to_surface $crv $srf $name $part]
	} 
	offset_within_surf {
	#format: crv srf pnt end ds=... de=...
		foreach "crv srf pnt end ds de" $def ""
		if [vid_exist "$this $method" curve $crv surface $srf point $pnt] return
		return 	[comak_offset_within_surf $part $name $crv $srf $pnt $end $ds $de] 
	return
	} 
	curve_end {
		# format: "POSITION" crv end pnt [junk]
		# or
		# format: "TANGENT" crv end vec [junk]

		set what [lindex $def 0]
		set c1 [lindex $def 1]
		set e1 [lindex $def 2]
		set p  [lindex $def 3]
		set xyz [ic_vset -vec - $p]
		if {$xyz == ""} {
			ic_mess "Err: vector $p\n"
			return
		}
		
		switch $what {
			"position" { 
			return 	[comak_curve_end_position $part $name $c1 $e1 $xyz] 
			}
			"tangent" { 
			return 	[comak_curve_end_tangent $part $name $c1 $e1 $xyz] 
			}
			default {
			ic_mess "Err. curve_end position $name [lindex $def 0]\n" red
			return
			}
		}
	}

	matching {
		# format: "TANGENT" crv_mod at_end crv_match at_end junk junk...
		# at_end may be xmax, ymax, zmin
		if {[lindex $def 0] != "tangent"} {
			ic_mess "Err. curve matching $name [lindex $def 0]\n" red
			return
		}
		set c1 [lindex $def 1]
		set e1 [lindex $def 2]
		set c2 [lindex $def 3]
		set e2 [lindex $def 4]
		return 	[comak_matching_curve_tangent $part $name $c1 $e1 $c2 $e2]
	}
	poly_line {
		# format: list of points/vector-expressions
		set n 0
		set xysz ""
		foreach p $def {
			set xyz [ic_vset -vec - $p]
			set xyz [ic_coords_into_global $xyz]
			if {$xyz == ""} {
				ic_mess "error in polyline point $p; ignored\n" red
				continue
			}
			incr n
			append xyzs " $xyz"
		}
		if {$n < 2} {
			ic_mess "Error too few points for poly_line $name\n" red
			return
		}
		ic_delete_geometry curve names $name 0
		ic_geo_new_family $part
		set knots 0
		for {set i 0} {$i < $n} {incr i} {
			lappend knots [expr double($i)/($n-1)]
		}
		lappend knots 1
		set com $projlib
		lappend com create_param_curve $name $part $n 2 0 $knots $xyzs 
		if [catch $com res] {
			ic_mess "DB error=$res\n" red
			return
		}
		$projlib configure -modified 1
		set bsp [define_geom_object curve $res]
		update_geom_visible_obj $bsp
		return [$bsp get -name]
	}
	point_list {
		# format pnt ...pnt [TAN1 vec] [TAN2 vec]
		set last_pnt 1000
		set nwords [llength $def]
		foreach v {1 2} {
			set vec$v ""
			set n [lsearch $def tan$v]
			if {$n > 0} {
			if {$last_pnt > $n} {set last_pnt $n}
			# tan.. definition may include several words:
				for {incr n} {$n < $nwords} {incr n} {
					set t [lindex $def $n]
					if {$t == "tan1" || $t == "tan2"} break
					lappend vec$v $t
				}
				# tan.. definition may be an expression: check value
				set vec$v [ic_vset -vec - [set vec$v]]
			}
		}
		if {$vec1 == "" && $vec2 == ""} {
			return [ic_geo_cre_bsp_crv_n_pnts $part $name $def $same_pnt_tol]
		}
		incr last_pnt -1
		if {$vec1 != ""} {
			set vecs [list $vec1]
			set inds 0

		} else {
			set vecs ""
			set inds ""
		} 
		if {$vec2 != ""} {
			lappend vecs $vec2
			lappend inds $last_pnt 
		} 
		set pts [lrange $def 0 $last_pnt]
		# it seems as ic_...cons function has some bugs
		# simulate with extra locations for now:
		if 0 {
			return [ic_geo_cre_bsp_crv_n_pnts_cons $part $name \
			$pts "all" $vecs $inds]
		} else {
			set p0 [lindex $pts 0]
			set pn [lindex $pts $last_pnt]
			set newlist [list $p0]
		# add extra point[s]
			if {$vec1 != ""} {
				set vec1 [ic_vcalc nrm $vec1]	
				set d  [ic_vcalc distance $p0 [lindex $pts 1]] 
				set v [ic_vcalc smult $vec1 [expr $d*0.1]]
				set p [ic_vcalc sum $p0 $v]
				lappend newlist $p
			}
			for {set i 1} {$i < $last_pnt} {incr i} {
				lappend newlist [lindex $pts $i]
			}
			if {$vec2 != ""} {	
				# comak tangent definition not consistent with med!
				# it seems, as a direction to curve center, not to the end
				set vec2 [ic_vcalc nrm $vec2]
				set i [expr $last_pnt - 1]	
				set d [ic_vcalc distance $pn [lindex $pts $i]]	
				set v [ic_vcalc smult $vec2 [expr -0.1*$d]]
				set p [ic_vcalc sum $pn $v]
				lappend newlist $p
			}
			lappend newlist $pn
			return [ic_geo_cre_bsp_crv_n_pnts $part $name $newlist $same_pnt_tol]
		}
	}
	isoparametric {
		# simulate comak command
		# format: surface point +/-X|Y|Z
		# a) project point on surface (psrf)
		# b) get u-v parameters
		# c) check which of 4 edge [surface] with that u|v has max/min X|Y|Z coord
		# d) create iso crv with the u|v
		# e) split crv at psrf
		# f) delete "wrong" part of crv
		# all done in geometry_subs.tcl comak_isoparametric 
		foreach "srf pnt dir" $def ""
		set dir [string toupper $dir]
		set res ""
		if [ic_geo_check_part $srf] {
			set srfs [ic_geo_get_objects surface $srf]
			set xyz [ic_vset -vec - $pnt]
			set srf [ic_geo_nearest_surface_list $xyz $srfs]
		}
		return [comak_isoparametric $part $name $srf $pnt $dir]
	}
	concat {
		# default - keep_old = 0
		if {![info exists keep_old] || ($keep_old != "1")} {set keep_old 0}
		ic_geo_modify_curve_reappr $def $tritol 0
		return [vid_concat_crvs $part $name $def $keep_old] 
	}
	ellipse {
	# format: center point_on_ell point_on_ell {optional start_a end_a} [vec vec vec]
	# this function may change main axis direction and definition of start-end angles
	# advantage: works even if direction of main axis is unknown
		foreach "c p1 p2 sa ea" $def ""
		set d1 [ic_vcalc distance $c $p1]
		set d2 [ic_vcalc distance $c $p2]
		set d12 [ic_vcalc distance $p1 $p2]
		if {$d1 == "" || $d12 == "" || $d1 == 0 || $d2 == 0 || $d12 == 0} {
			ic_mess "Err: ellipse $name :$def\n" red
			return
		}
	# wierd restriction or bug in ic_geo_cre_crv_ell if d1==d2
		if {$d1 == $d2} {
			# create 3-rd point (location) on arc between p1 & p2 
			# and create arc from 3 points
			set p12 [ic_vcalc sum_smult $p1 $p2 0.5]
			set vec [ic_vcalc diff $c $p12]
			set add [ic_vcalc nrm_smult $vec $d1]
			set padd [ic_vcalc sum $c $add]
			# degenerated bspline may create error:
			set err [catch {ic_geo_cre_arc_from_pnts $part $name $p1 $padd $p2} res]
			if $err return else {return $res}
		}
		if {$d1 > $d2} {
			set e [list $c $p1 $p2]
		} else {
			set e [list $c $p2 $p1]
		}
		if {[llength $def] == 5} {
			lappend e $sa $ea
		}
		return [eval "ic_geo_cre_crv_ell $part $name $e"] 
	}
	split {
	    # split does not fit format: name only valid for one split; may be more 
	    # format: crv vec [vec ... vec]
		set crv [lindex $def 0] 
			set ppts ""
			foreach pnt [lrange $def 1 end] {
				# check with next command: allow vector expressions and variables
				if [catch "format {%g %g %g} $pnt"] {
					set pnt [ic_get_point_location $pnt]
					if [catch "format {%g %g %g} $pnt"] {
						ic_mess "Err: point\n" red
					    continue
				    }
				}
                set curLCS [ic_csystem_get_current]
                if {$curLCS != "global"} {
                    #set xyz [ic_coords_into_local $pnt]
                    set xyz $pnt
                    set p [ic_coords_into_local [ic_geo_project_coords curve $crv $xyz {0 0 0}]]
                } else {
				    set xyz $pnt
				    set p [ic_geo_project_coords curve $crv $xyz {0 0 0}]
				}
				set u [ic_geo_find_nearest_crv_pnt $crv $p]
				if {$u < 0.001 || $u > 0.999} {
					ic_mess "Cannot split curve at \"end point\" $p\n" red
					continue
				}
				set part [ic_geo_get_family curve $crv]
				lappend ppts [ic_geo_cre_pnt $part tmp.00 $p]
			}
			set ret [catch {ic_geo_split_curve $crv $ppts} res]
			ic_delete_geometry point names $ppts 0
			if !$ret {
				if ![llength [ic_geo_check_objects_exist curve $crv]] {
					ic_geo_set_name curve [lindex $res 0] $crv	
					ic_geo_set_name curve [lindex $res 1] $name
					return "$crv $name [lrange $res 2 end]" 						
				}				
			}
			return $res
	}
	copymove {
		set sr [lindex $def 0]
		if [ic_geo_check_family $sr] {
			set sr [lindex [ic_geo_get_objects curve $sr] 0]
			if {$sr == ""} return
		}
		set newc [ic_geo_duplicate curve $sr]
		ic_geo_set_name curve $newc $name		 
		ic_geo_set_family curve $part names $name 0		 
		eval ic_move_geometry curve names $name [lrange $def 1 end]
		return $name
	} 
	trim_side {
	# ? connect end of crv1 with projection of the end on crv1?
	# format: crv1 crv2 how
	# there is other format: crv1 pnt1 how!
	# as in comak it delete crv1!!!
		foreach "crv1 something how" $def ""
		set xyz [ic_geo_get_crv_end_generic $crv1 $how]
		set something_type [string trim [ic_vset -med_entity $something]]
		if {$something_type == "curve"} {
			set xyz2 [ic_geo_project_coords curve $something $xyz "0 0 0" 1]
		} elseif {$something_type == "point"} {
			set xyz2 [ic_geo_get_point_location $something]
		} else {
			ic_mess "Error in trim_end definition $def\n"
			return
		}
		ic_delete_geometry curve names $crv1 0
		return [ic_geo_cre_bsp_crv_n_pnts $part $name [list $xyz $xyz2] $same_pnt_tol]
	} 
	va_arc {
		set res [eval ic_geo_cre_va_arc $part $name $def]
		return $res
	}
	trim_both_ends {
	# format: crv pnt pnt
		foreach "crv p1 p2" $def ""
		if {[string trim [ic_vset -med_entity $crv]] != "curve"} {
			ic_mess "Err trim_both_ends curve $crv not found:$def\n" red
			return
		}
		set ps [ic_geo_get_crv_pos 0 $crv]
		set pe [ic_geo_get_crv_pos 1 $crv]
		set len [ic_geo_crv_length $crv]
		set spltol [expr 0.001*$len] 
		set newcrvs [ic_geo_split_curve $crv "$p1 $p2"]
		set keep ""
		foreach crv $newcrvs {
			set pa [ic_geo_get_crv_pos 0 $crv]
			set pb [ic_geo_get_crv_pos 1 $crv]
			set del 0
			foreach "pp1 pp2" "$ps $pa $ps $pb $pe $pa $pe $pb" {
				set dd [ic_vcalc distance $pp1 $pp2]
				if {$dd < $spltol} {
					set del 1
					break
				}
			}
			if $del {
				ic_delete_geometry curve names $crv 0
				continue
			}
			set keep $crv			
		}
		if {$keep != ""} {
			ic_geo_set_name curve $keep $name
			return $name
		}
		ic_mess "Err: trim both: $def\n"
		return ""
	}
	trim_middle {
	# format: name1 name2 crv pnt how; how = ZMIN/ZMAX etc.
	# or
	# format: name1 name2 crv crv2 how; how = ZMIN/ZMAX etc.

		foreach "name1 name2 crv something how" $def ""
		set xyz [ic_geo_get_crv_end_generic $crv $how]
		if ![llength $xyz] {
			ic_mess "Error: curve_end crv=$crv how=$how\n"
			return ""
		}
		set pnt ""
		set sometype [string trim [ic_vset -med_entity $something]]
		if {$sometype == "curve"} {
			set n1 curve:[$projlib get_number curve $crv]
			set n2 curve:[$projlib get_number curve $something]
		    set p [$projlib intersect_curves "$n1 $n2" 0.1]
			if ![llength $p] {
				ic_mess "Err. trim middle crv-crv intersect\n" red
				return
			}
			set pnt [lindex $p 0]
		}
		if {$sometype == "point"} {
			set pnt $something
		}
		if {$pnt == ""} {
			ic_mess "Err. trim middle unknown type\n" red
			return
		}
		ic_delete_geometry curve names "$name1 $name2" 0	
		set nms [ic_geo_split_curve_at_point $crv $pnt 0.01]
		if {[llength $nms] != 2} {return ""}

# Check with COMAK specialists about curve order 
		foreach {newc1 newc2} $nms {
		# must rename to some temporary name. 
		# Reason: ic_geo_split_curve can create names which are requested.
		#  In this case "rename" command will not work properly 
			ic_delete_geometry curve names "tmp_comak_crv1 tmp_comak_crv2" 0
			ic_geo_set_name curve $newc1 tmp_comak_crv1	
			ic_geo_set_name curve $newc2 tmp_comak_crv2
			set newc1 tmp_comak_crv1
			set newc2 tmp_comak_crv2													 
		}
# set first curve which ends has min dist (0) to $how point:
		set xyz1 [ic_geo_get_crv_end_generic $newc1 $how]
		set xyz2 [ic_geo_get_crv_end_generic $newc2 $how]
		set d1 [ic_vcalc distance $xyz $xyz1]
		set d2 [ic_vcalc distance $xyz $xyz2]
		if {$d1 < $d2} {		
			ic_geo_set_name curve $newc1 $name1
			ic_geo_set_name curve $newc2 $name2
		} else {
			ic_geo_set_name curve $newc1 $name2
			ic_geo_set_name curve $newc2 $name1
		}
		return [list $name1 $name2]
	}
	arc_ctr_rad {
		foreach "c p1 p2 r sa ea va_arc" $def ""
#		c, p1 p2 are  triplets in global

		set sa [ic_vset - $sa]
		set ea [ic_vset - $ea]
		if {$sa == ""} {set sa 0}
	    if {$r == 0.0} {
	        set r [ic_vcalc distance $c $p1]
	    }
		set r [ic_vset - $r]
		# for regression test with missing start/end angle:
		# format : c x-direction, n-direction radius 
		if {$ea == "" && $va_arc == ""} {
			set x [ic_vset -vec - $p1]
			set n [ic_vset -vec - $p2]
			if {$x == "" || $n == "" || $r == ""} return		
			set c [lindex [va_set_aux_pnts $part ${name}.00 [list $c] $auxiliary $vid_mode] 0]
			if {$c == ""} { return "" }
			return [ic_geo_cre_crv_arc_ctr_rad $part $name $c $x $n $r 0 360]
		}
				
		foreach "c p1 p2" [va_set_aux_pnts $part ${name}.00 [list $c $p1 $p2] $auxiliary $vid_mode] ""
		# Validation:
		set x [ic_vcalc dir $c $p1]
	    set y [ic_vcalc dir $c $p2]
	    set n [ic_vcalc product $x $y]
		if {$c == "" || $x == "" || $n == ""} {
			status_message "Error in curve/arc data" red
			return
		} 
		if ![ic_vcalc len $n] {
				status_message "Error: Selected points are on the same line" red
				return
		}
		if {$ea == ""} {set ea [ic_vcalc angle $x $y]}
		if {$sa < 0} {set sa [expr -($sa)]}
		if {$ea < 0} {set ea [expr -($ea)]}		
		if {$sa == $ea} {set ea [expr $sa + 360]} 
		if {$r  == ""} {set r [ic_vcalc distance $c $p1]}
		#
		if {$va_arc == "1"} {
			return [ic_geo_cre_va_arc $part $name $p1 $p2 $c $r]
		}
		return [ic_geo_cre_crv_arc_ctr_rad $part $name $c $x $n $r $sa $ea]
		
	}
	intersect {
		set nsrfs [llength $def]
		if {$nsrfs != 2} {
			# name has not been used
			return [vid_intersect_with_bbox $part $def]
		}
		# if nsrfs =2, it may be part name or surface name 
		foreach "srf1 srf2" $def ""
		set group 0
		if [ic_geo_check_part $srf1] {
			incr group
			set srf1 [ic_geo_get_objects surface $srf1]
		} elseif {
			[ic_geo_check_objects_exist surface $srf1] == ""
		} return
		if [ic_geo_check_part $srf2] {
			set srf2 [ic_geo_get_objects surface $srf2]
			incr group 2
		} elseif {
			[ic_geo_check_objects_exist surface $srf2] == ""
		} return
		#
		#group may be 0 1 2 or 3
		switch $group {
		1 -
		2 -
		3 {
		set crvs [ic_geo_intersect_surfs_by_groups [list $srf1 $srf2] $part use_bsp]
		if {[llength $crvs] > 1} {
			set err [catch {ic_geo_cre_crv_concat $part $name $tol $crvs} res]
			if $err {
				ic_mess "Error $res\n" red
				return 
			}
			ic_delete_geometry curve names $crvs
			return $name
		} 
		ic_geo_set_name curve $crvs $name
		return $name
		}
		0 {
		set crv [ic_geo_intersect_surfaces $part "$srf1 $srf2" use_bsp]
		}
		}
		if {[llength $crv] == 1} {
		    if {[ic_geo_check_objects_exist curve $crv] == ""} {
			    status_message "Error: no intersection: $def" red
			    return ""
		    }
		    ic_geo_set_name curve $crv $name
		    ic_geo_set_family curve $part names $name 0	
		    return $name
        } elseif {[llength $crv] > 1} {
            ic_geo_set_family curve $part names "$name" 0
            return $crv
        } else {
            return ""
        }

	}
	
    circle {
   # new function 
   # Input: 2 or more locations on curve to define circle: 2-screen plane; 3- exact; more-average
   set pts $def
   set npts [llength $pts]
   if {$npts < 2} return
   set factor [expr 1.0/$npts]
   if {$npts == 2} {
      set center [eval ic_vcalc sum_smult $pts 0.5]
   } elseif {($npts == 3) && ![info exists maximum_radius]} {
     set center [eval ic_vcalc center $pts]
   } else {
      set center {0 0 0}
      foreach p $pts {
        set center [ic_vcalc sum $center $p]
      }
      set center [ic_vcalc smult $center $factor]
   }
   if {$center == ""} return       
   
   # Set radius as average or maximum (args = "maximum_radius 1") distance from center 
   set radius 0.0
   if [info exists maximum_radius] {
    foreach p $pts {
       set r  [ic_vcalc distance $center $p]
       if {$r > $radius} {set radius $r}
    }
   } else { 
   foreach p $pts {
      set radius [expr $radius + $factor*[ic_vcalc distance $center $p]]
   }
   }
   # set x_ax as a direction from center to first points
   set p1 [lindex $pts 0]
   set x_ax [ic_vcalc dir $center $p1] 
   
   # calculate normal as non-zero product to any point except first
   # if not found (all points inline), use screen normal (if available) 
   set pts [lreplace $pts 0 0]
####   if {$npts == 2} {set pts [list $pts]}
   set found 0
   foreach p $pts {
     set normal [ic_vcalc product $center $p1 $p]
     set nlen [ic_vcalc len $normal]
     if {$nlen > 0.00001} { set found 1; break}
   }
   if !$found {
     global tdv_default
     if ![info exists tdv_default] return
     set normal [lindex [$tdv_default screen2world 100 100] 1]
   }
   set normal [ic_vcalc nrm $normal] 
   # may be optional argument to return circle only data: "only_data 1"
   if [info exists only_data] {
     return [list $center $x_ax $normal $radius]   
   }
   ic_geo_cre_crv_arc_ctr_rad $part $name $center $x_ax $normal $radius 0 360
   return [list $name $center $x_ax $normal $radius]
   #end circle   
}
	
	arc - 
	default {
# create points, if requested, for arc and default:
# these are only two methods, which use only "points"
		set ne [expr [llength $def]-1]
		if {$ne < 1} return
		set name_for_pnts ${name}.00
		set newdef ""
		#  
		# if point name - use name [point location] 
		# if other valid definition - convert to LCS
		# ic_geo_check_objects_exist may return wrong result for points...

		for {set i 0} {$i <= $ne} {incr i} {
			set pnt [lindex $def $i]
			if {[lindex [ic_vset -med_entity $pnt] 0] == "point"} {
				# this is name of med point. update location if there is a virtual definition
				# this [point] type in Vid called "vector" in order to differ from med point
				if {[ic_vset -type $pnt] == "vector"} {
						set xyz [ic_vset $pnt]
						if {$xyz != ""} {ic_set_point_location $pnt $xyz}
				}	
				lappend newdef $pnt
			} else {
				# 
				set xyz [ic_vset -vec - $pnt]
				if {$xyz == ""} {
				status_message "Error in ic_curve definition: $pnt"
				return
			} 
				set xyz [ic_coords_into_global $xyz]				
				# may be create points: Use names of these points
				if $cre_pnts {		
					if  {($cre_pnts == 2) || !$i || ($i == $ne)} {		
						# create new point.from expression or triplet "pnt"
						# ic_point will create point using LCS
						set pname [ic_vset -new_name $name_for_pnts]
						set p [ic_point "" $part $pname $pnt]
						if {$p == ""} {
							status_mesage "Error in ic_curve definition: $pnt"
							return
						}
			lappend newdef $p  					
						continue
					}
				}				
				lappend newdef $xyz
			}			
		}
		if {$method == "arc"} {
			if {[llength $newdef] != 3} {
				status_message "Set/select 3 points" red
				return
			} 
			# degenerated bspline may create error:
			set err [catch {eval ic_geo_cre_arc_from_pnts $part $name $newdef} res]
			if $err return else {return $res}
		}	
		if {[llength $newdef] < 2} {
				status_message "Set/select at least 2 points" red
				return
		}			 			 					 
		return [ic_geo_cre_bsp_crv_n_pnts $part $name $newdef $same_pnt_tol]
	}
	}
}
#================================================
proc ic_surface {method part name def args} {
#
# Modified:
# 03/22/06 set tol as geo_cad(toler) if defined  
global projlib
if ![regexp {[a-zA-Z]} [string index $name 0]] {
  set name S$name
}
# default args:
	set tol [ic_gui_set geo_cad(toler)]
	if {$tol == 0} {set tol 0.001}
        set btopo [ic_gui_set vid_options(new_srf_topo)]
	set vid_mode [ic_gui_set vid_options(vid_mode)]
	set replace [ic_gui_set vid_options(replace)]  

foreach {n v} $args {set $n $v}
update idletasks
# 
# method : one of curve[s], copym[ove], of_re[volution]
# part : family name
# name : surface name
# def  : definition, format is method-dependent
#
#
#********************************************************
# Delete old entity with the same name or create new name?    
# feature will be not deleted because feature name is a curve name 
    if {$replace || $vid_mode} {
		ic_delete_geometry surface names $name 0
	} else {
		set name [ic_vset -new_name $name all]
	}
#******************************************************
#
#
	switch $method {
		reappr {
			#format: part name (1 word) or list of surfaces (>= 1 word)
			# external: tol == geo_cad(toler) == topo tolerance
			#         : btopo == vid_options(new_srf_topo)
			# a) build topo 
			# b) reappr
			# c) set name
			# d) delete new topology curves and points if !$btopo
			if {([llength $def] == 1) && [ic_geo_check_family $def]} {
				set def [ic_geo_get_objects surface $def]
			}
			if {$def == ""} {
				status_message "No surfaces" red
				return
			}
			# 03/22/06 DO NOT REAPPR IF ONE SURFACE. TESTING DB DB 
			if {[llength $def] == 1} {return $def}
			
			set is_topo 1
			set topo_crvs ""
			foreach srf $def {
				set crvs [ic_geo_boundary surface $srf 0 1]
				if {$crvs == ""} {
					set is_topo 0
				} else {
					append topo_crvs " $crvs"
				}
			}

			if !$is_topo {
				set com [list ic_build_topo $tol -surfaces $def]
				if [llength $topo_crvs] {lappend com -curves $topo_crvs}
				eval $com
			}
			set newsrfs [ic_geo_modify_surface_reappr $def $tol 0]

			if {$newsrfs == "{}"} {
				status_message "Can't reapproximate $def" red
				return
			}
			if !$btopo {
				# delete NEW topo curves of new surfaces
				if [array exists tmp_crvs] {unset tmp_crvs}
				if [array exists tmp_crvs] {unset tmp_crvs}				
				foreach srf $newsrfs {
					foreach crv [ic_geo_boundary surface $srf 0 1] {
						set tmp_crvs($crv) 1
					}
				}
				foreach crv [array names tmp_crvs] {
					foreach pnt [ic_geo_boundary curve $crv 0 1] {
						set tmp_pnts($pnt) 1
					}						
					if {[lsearch $topo_crvs $crv] >= 0} {unset tmp_crvs($crv)}
				}
				ic_delete_geometry curve names [array names tmp_crvs]
				ic_delete_geometry point names [array names tmp_pnts]				
			}
			# set name(s)
			set res ""
			foreach srf $newsrfs {
					ic_geo_set_name surface $srf $name
					ic_geo_set_family surface $part names $name 0	
					lappend res $name	
					set name [ic_vset -new_name $name.S1 all]
			}
			return $res
		}
		hole {
		# this function does not create surface, only remove/create hole on existing
		# name is a feature (one curve) name
		foreach "srf xyz rad" $def ""
		if {[catch "ic_geo_get_object_type surface $srf" type] || ($type != "param")} {
			status_message "Err: require bspline surface"
			return
		}
		if ![ic_is_triplet $xyz] {
			status_message "Err: no location for hole"
			return
		}		
		if {[ic_geo_check_objects_exist curve $name] == $name} {
			ic_geo_remove_feature $name
		}
		if [catch {ic_geo_find_nearest_srf_pnt $srf $xyz} uv] {
			status_message "Err: wrong location for hole"
			return
		}
		# rad may be expression/variable
		set r [ic_vset - $rad]
		if {$r == ""} {
			status_message "Err: radius not defined"
			return
		}						
	    foreach "u v" $uv ""
	    set nrm [lindex [ic_geo_get_srf_nrm $u $v $srf] 0]
	    set xdir [ic_vcalc vec_norm $nrm]
		set tmpname [ic_geo_cre_crv_arc_ctr_rad $part tmpname $xyz $xdir $nrm $r 0 360]
		set crkl [ic_geo_project_curve_to_surface tmpname $srf $name $part]
		ic_delete_geometry curve names tmpname 0
		if {[llength $crkl] != 1} {
		ic_delete_geometry curve names $crkl 0
			status_message "Hole radius too big for the surface." red
			return
		}
		set res [ic_geo_trim_surface $srf $crkl]
		ic_delete_geometry surface names ${srf}.cut.0 0
		return $crkl
	} 
	4pts {       
		# order of points: clockwise or ccwise
		if {[llength $def] != 4} {
			status_message "Must use 4 points/locations to create surface"
			return
		}
		foreach n "0 1 2 3" {
			set xyz [ic_vset -vec - [lindex $def $n]]
			if {$xyz == ""} {
				status_message "Error in 4pts corner point $n $def\n" red
				return
			}			
			set xyz [ic_coords_into_global $xyz]
			set p$n $xyz
		}
		# Some surface sides may collapse, but not all:	
		if {([ic_vcalc distance $p0 $p1] < $tol) &&\
			([ic_vcalc distance $p1 $p2] < $tol) &&\
			([ic_vcalc distance $p2 $p3] < $tol)} {
				status_message "Surface sides are too small. Check topology tolerance" red
				return
		}
		set xyzs "$p0 $p1 $p3 $p2"		
		set com ic_geo_create_param_surface 
		lappend com $part $name 2 2 2 2 0 {0 0 1 1} {0 0 1 1} $xyzs ""

		# use new name and existing part: this function does not have validation
		ic_delete_geometry surface names $name 0
		ic_geo_new_family $part
		eval $com
		if {$btopo && ($name != "")} {
			ic_build_topo $tol -surfaces $name -quiet
		}
		return $name
	}
	2sides {
	# format: point normal D1 D2, where normal == X or Y or Z, D1, D2 - scalar expression 
	# this function is a "surface 4pts" defined with Dx, Dy
	#
		set xyz [ic_vset -vec - [lindex $def 0]]
		set normal              [lindex $def 1]
		set D1  [ic_vset      - [lindex $def 2]]
		set D2  [ic_vset      - [lindex $def 3]]
		if {$xyz == "" || $D1 == "" || $D2 == "" || $D1 == 0 || $D2 == 0} {
			status_message "Error in surface definition"
			return
		}
		foreach "x y z" $xyz "" 
		switch $normal {
			X {
		set p1 [ic_vcalc sum $xyz 0,$D1,0]
		set p2 [ic_vcalc sum $xyz 0,$D1,$D2]
		set p3 [ic_vcalc sum $xyz   0,0,$D2]
		}
			Y {
		set p1 [ic_vcalc sum $xyz $D1,0,0]
		set p2 [ic_vcalc sum $xyz $D1,0,$D2]
		set p3 [ic_vcalc sum $xyz   0,0,$D2]
		}
		default {					
		set p1 [ic_vcalc sum $xyz $D1,0,0]
		set p2 [ic_vcalc sum $xyz $D1,$D2,0]
		set p3 [ic_vcalc sum $xyz   0,$D2,0]
		}
		}
		set xyzs "$xyz $p1 $p3 $p2"	
		set com ic_geo_create_param_surface							
		lappend com $part $name 2 2 2 2 0 {0 0 1 1} {0 0 1 1} $xyzs ""
		ic_delete_geometry surface names $name 0
		ic_geo_new_family $part
		eval $com
		if {$btopo && ($name != "")} {
			ic_build_topo $tol -surfaces $name -quiet
		}
		return $name			 
	} 
	ruled {
		# connect 0-0 and 1-1 points of 2 curves
		# it creates extra 2 curves
		foreach "c1 c2" $def ""
		if {[ic_geo_check_objects_exist curve $c1] == ""} return
		if {[ic_geo_check_objects_exist curve $c2] == ""} return
		set e10 [lindex [ic_geo_get_crv_pos 0 $c1] 0]
		set e11 [lindex [ic_geo_get_crv_pos 1 $c1] 0]
		set e20 [lindex [ic_geo_get_crv_pos 0 $c2] 0]
		set e21 [lindex [ic_geo_get_crv_pos 1 $c2] 0]
		set cc0 [ic_geo_cre_line $part ${name}.01 $e10 $e20]
		set cc1 [ic_geo_cre_line $part ${name}.02 $e11 $e21]
		lappend def $cc0 $cc1
		set res [ic_geo_create_surface_from_curves $part $name $tol $def 0]
		return $res
	}
	plane_pt_vec {
		#format: pnt normal length rad 
		# ic_geo_cre_srf_pln_3pnts does not work. E.g.:
		# ic_geo_cre_srf_pln_3pnts AA p3 {9 9 0} {10 9 0} {9 9.1 0} 1
		#Error: {Unable to convert cylinder to bspline} {Plane points are collinear}
		# Does not worth to spend time now. Fix in icm4bspsrfpln.C 
		foreach {pnt normal how rad} $def ""
		if {$how != "length"} {
			ic_mess "Unknown option for plane_pt_vec: $how\n" red
			return
		}
		if {$rad == "auto_size"} {
			set rad [eval ic_vcalc distance [ic_geo_model_bounding_box]]
		}
		set nrm [ic_vset -vec - $normal]
		if {$nrm == ""} {
			ic_mess "Can't find normal plane_pt_vec: $normal\n" red
			return
		}	
		ic_geo_cre_srf_pln_nrm_dist $part $name $nrm 0 $rad
		ic_move_geometry surface names $name translate $pnt
		return $name
	}
	plane_3_points {
		# format: pnt pnt pnt "CENTER" pnt "LENGTH" value
		foreach "p1 p2 p3 centkey pc lenkey rad" $def ""
		set nrm [ic_vcalc product $p1 $p2 $p3]
		if {$rad == "auto_size"} {
			set rad [eval ic_vcalc distance [ic_geo_model_bounding_box]]
		}
		if {$nrm == ""} {
			ic_mess "Can't find normal plane_pt_vec: $normal\n" red
			return
		}	
		ic_geo_cre_srf_pln_nrm_dist $part $name $nrm 0 $rad
		ic_move_geometry surface names $name translate $pc
		return $name			 
	}
	plane_coefficients {
		# something wierd: center defined in coeff (d) and as pc
		# using pc 
		foreach "coeff centkey pc lenkey rad" $def ""
		foreach "a b c d" [split $coeff ,] ""
		if {$rad == "auto_size"} {
			# next method does not work if plane defined in empty tetin
			# set rad [eval ic_vcalc distance [ic_geo_model_bounding_box]]
			set rad 1000
		}
		set nrm "$a $b $c"
		ic_geo_cre_srf_pln_nrm_dist $part $name $nrm 0 $rad
		ic_move_geometry surface names $name translate $pc
		return $name
	}
	copymove {
		set sr [lindex $def 0]
		if [ic_geo_check_family $sr] {
			set sr [lindex [ic_geo_get_objects surface $sr] 0]
			if {$sr == ""} return
		}
		set newc [ic_geo_duplicate surface $sr]
		ic_geo_set_name surface $newc $name	
		ic_geo_set_family surface $part names $name 0			
		eval ic_move_geometry surface names $name [lrange $def 1 end]
		return $name
	} 
	sphere {
		#
		foreach "c p1 r sa ea" $def ""
		set c [ic_coords_into_global [ic_vset -vec - $c]]
		set p1 [ic_coords_into_global [ic_vset -vec - $p1]]		
		set x [ic_vcalc dir $c $p1]
		if {$x == "0 0 0"} {
				status_message "Error: can't define sphere axis" red
				return
		}			
	    set y [ic_vcalc vec_norm $x]
	    set n [ic_vcalc product $x $y]
		set r [ic_vset - $r]
		if {$r == "" || ($r <= 0)} {
				status_message "Error: sphere radius not defined" red
				return
		}			
		set sa [ic_vset - $sa]
		set ea [ic_vset - $ea]
		foreach z "c x n" {
			if ![llength [set $z]] {
				status_message "Error in sphere data" red
				return
			}
		}
#		default:
		if {$sa == ""} {set sa 0}
		if {$ea == ""} {set ea 180}
		if {$sa < 0} {set sa [expr -($sa)]}
		if {$sa >= 180} {set sa 0}
		if {$ea < 0} {set ea [expr -($ea)]}		
		if {$sa == $ea} {set ea 180} 
		set crvname [ic_vset -new_name $name.C0 all]
		set crvname [ic_geo_cre_crv_arc_ctr_rad $part $crvname $c $x $n $r $sa $ea]
		set srf [ic_geo_cre_srf_rev $part $name $crvname $c $x 0 180 c 0]
		set srf1 [ic_geo_cre_srf_rev $part $name.1 $crvname $c $x 180 360 c 0]
		if $btopo {
			ic_build_topo $tol -curves $crvname -surfaces "$srf $srf1"
		}		
		if {!$btopo || $srf == ""} {
			ic_delete_geometry curve names $crvname 0
		}
		return "$srf $srf1"
	}
	cyl {
		# LCS Ok
		foreach "p1 p2 r1 r2 base1 base2" $def ""
		set p1 [ic_coords_into_global [ic_vset -vec - $p1]]
		set p2 [ic_coords_into_global [ic_vset -vec - $p2]]		
		set axis [ic_vcalc dir $p1 $p2]
		if {$axis == "" || $axis == "0 0 0"} { 
			status_message "Axis not defined" red
			return
		}
		# r1 r2 base1 base2 may be scalar expressions
		foreach e "r1 r2 base1 base2" {
			set v [ic_vset - [set $e]]
			if {$v == "" || $v < 0} {
				status_message "Cylinder value $e not defined" red
				return
			}			
			set $e $v
		}
		if {$r1 == 0 || $r2 == 0} {
			status_message "Radius must be > 0" red
			return
		}						
		set x [ic_vcalc vec_norm $axis]
		set crv1 [ic_vset -new_name $name.C0 all]		
		set crv1 [ic_geo_cre_crv_arc_ctr_rad $part $crv1 $p1 $x $axis $r1 0 360]
		set crv2 [ic_vset -new_name $name.C0 all]		
		set crv2 [ic_geo_cre_crv_arc_ctr_rad $part $crv2 $p2 $x $axis $r2 0 360]	

		if {$base1 != "0"} {
			set srf1 [ic_vset -new_name $name.S1 all]			
			ic_geo_gap_repair toler $tol build_topo 0 new_family $part new_name $srf1 curves $crv1 do 3 partial 0 yellow 0 quiet 1 db -1
		}
		if {$base2 != "0"} {
			set srf2 [ic_vset -new_name $name.S2 all]			
			ic_geo_gap_repair toler $tol build_topo 0 new_family $part new_name $srf2 curves $crv2 do 3 partial 0 yellow 0 quiet 1 db -1
		}
		set srf [ic_geo_create_surface_from_curves $part $name $tol "$crv1 $crv2" $btopo]		
		set res ""
		foreach s "srf srf1 srf2" {
			if [info exists $s] {lappend res [set $s]}
		}
		if {!$btopo || $srf == ""} {
			ic_delete_geometry curve names "$crv1 $crv2"
		} else {		
			ic_build_topo $tol -surfaces $res -curves "$crv1 $crv2" -quiet
		}
		return $res			
	}
	of_revolution {
		#FORMAT crv base zaxis srtang endang dxn btopo
		foreach "crv base zaxis sa ea dxn btopo" $def ""
		set base [ic_vset -vec - $base]
		set zaxis [ic_vset -vec - $zaxis]	
		set sa [ic_vset - $sa]
		set ea [ic_vset - $ea]
		foreach res "base zaxis sa ea" {
			if {[set $res] == ""} {
				ic_mess "Error srf rev $res: $def\n" red
				return
			}
		}
		set zaxis [ic_vcalc diff $base $zaxis]
		if ![ic_vcalc len $zaxis] {
			ic_mess "Err. creating $name:\n$def\n Can't rotate curve around \"0 0 0\" vector\n" red
			return
		}
		lappend com ic_geo_cre_srf_rev $part $name $crv $base $zaxis $sa $ea $dxn $btopo					 
	    set res [eval $com]
	    if {$btopo && ($res != "")} {
			ic_build_topo $tol -surfaces $res -curves $crv -quiet
		}
	    return $res
	} 
	2-4crvs {
		# format tol crvs
		foreach "tol crvs" $def ""
		set ret [catch {ic_geo_create_surface_from_curves $part $name $tol $crvs 0} res]
		if $ret {
		    return ""
		} else {
		    if $btopo {
		        ic_build_topo $tol -surfaces "$res" -curves "$crvs" -quiet
		    }
		    return $res
		}
    }
	multiple {
	 # format part name curves Do1 Do2 Do3
	   foreach "curves Do1 Do2 Do3" $def "" 
	   return [vid_find_multiple_loops $part $name $curves $Do1 $Do2 $Do3]
	}
	 bsinterp -
	 default {
		# simulation of comak/ddn command: may be gaps between curves 
		# function works only for new name
	 	set name [ic_vset -new_name $name all]
		set res [comak_bsinterp_srf_from_crvs $part $name $def]
		if {$btopo && ($res != "")} {
			ic_build_topo $tol -surfaces $res -curves $def -quiet
		}
		return $res 
	}
	}
}
#---------------------
#
#================================================
proc ic_modify {method type names args} {
#
# Under construction. Open-end container for generic functions 
# This procedure does not delete existing entity and does not create new
#   (like, e.g., ic_surface), but can change it.
#  Usage now: ic_modify reverse surface A 
#  
	if ![llength $names] return

	foreach name $names {
		vic_geo_modify $method $type $name $args
	}

	return 1
}

proc ic_geo_get_crv_end_generic {crv how} {
# Expect how: one of xmax||xmin||ymax...||zmin or "0"||"1"
if {$how == "0" || $how == "1"} {
	return  [lindex [ic_geo_get_crv_pos $how $crv] 0]
}
	set how [string toupper $how]
	set index [string first [string index $how 0] "XYZ"]
	if {$index < 0} {return ""}
	set minmax [lsearch "MIN MAX" [string range $how 1 3]]
	if {$minmax < 0} {return ""}
	if ![llength [ic_geo_check_objects_exist curve $crv]] {return ""}
#
# Note: ic_geo_get_crv_pos return list of points
	set e0 [lindex [ic_geo_get_crv_pos 0 $crv] 0]
	set e1 [lindex [ic_geo_get_crv_pos 1 $crv] 0]
	if $minmax {
		# looking for max
		if {[lindex $e0 $index] > [lindex $e1 $index]} {
			set xyz $e0
		} else {
			set xyz $e1
		}
	} else {
		# looking for min
		if {[lindex $e0 $index] <= [lindex $e1 $index]} {
			set xyz $e0
		} else {
			set xyz $e1
		}
	}
	return $xyz
}
#======================
proc ic_geo_cre_va_arc {part name p1 p2 c r} {
# c is a point on arc plane in ~ center direction
  set mid [ic_vcalc sum_smult $p1 $p2 0.5]
  set nrm  [ic_vcalc product $c $p1 $p2]
  set dir2 [ic_vcalc dir $mid $p2]
  set base [expr 0.5*[ic_vcalc distance $p1 $p2]]
  if {$base >= $r} {
    status_message "Radius less than distance between points" red
	return 
}
  set dir  [ic_vcalc product $dir2 $nrm]
  set dd   [expr $r-sqrt($r*$r-$base*$base)]
  set ddir [ic_vcalc nrm_smult $dir $dd]
  set try1 [ic_vcalc sum $mid $ddir]
  set try2 [ic_vcalc diff $ddir $mid] 
  set d1   [ic_vcalc distance $c $try1]
  set d2   [ic_vcalc distance $c $try2]
  if {$d1 < $d2} {set p12 $try2} else {set p12 $try1}
  # degenerated bspline may create error:
  set err [catch {ic_geo_cre_arc_from_pnts $part $name $p1 $p12 $p2} res]
  if $err return else {return $res}
}

#
# replace entity name[s] in tetin file without calling med
# body of the function may be replaced with projlib command 
# current usage: comak interpreter 
# args: {type newname [newfam] [oldname]} ... {type newname [newfam] [oldname]}
# 
# if oldname missing, set name for 1-st... n-th entity of the type
# 2005/09/21: new arguments
#  a) if "delete_others" == 1 -> delete from tetin file all entities, which are not in the list
#  b) new argument "fileout" is a file name for result. Must be different from "filein"
proc ic_geo_file_rename_entities {filein fileout delete_others lists} {
	if ![file exists $filein] return
	file delete -force $fileout
	set fin [open $filein "r"]
	set fout [open $fileout "w"]
	set n_rename 0
	set do_copy_string 1
	foreach ren $lists {
		if {[llength $ren] < 3} continue
		set ren_type [lindex $ren 0]
		set ren_new_name [lindex $ren 1]
		set ren_new_part [lindex $ren 2]
		set ren_part($ren_new_name) $ren_new_part
		if {[llength $ren] == 3} {
			set rename_by_number($ren_type,$n_rename) $ren_new_name

		} else {
			set [subst $ren_type]([lindex $ren 3]) $ren_new_name
		}
		incr n_rename
	}
	while {  [gets $fin txt ] >= 0 } {
		set word [lindex $txt 0]
		switch $word {
		prescribed_point {
			set line_type point
			#analyze line
		}
		define_surface {
			set line_type surface
			#analyze line			
		}
		define_curve {
			set line_type curve
			#analyze line				
		}
		define_model {
			set do_copy_string 1
			puts $fout $txt
			continue
		}
		default {						
			if $do_copy_string {
			puts $fout $txt
			}
			continue
		}
		}
		# analyze line
		set newname ""
		if [info exists entity_counter($line_type)] {
			incr entity_counter($line_type)
		} else {
			set entity_counter($line_type) 0
		}
		set n $entity_counter($line_type)
		set i_name [lsearch $txt "name"]
		set i_fam [lsearch $txt "family"]
		incr i_name
		incr i_fam
		set line_name [lindex $txt $i_name]
		if [info exists rename_by_number($line_type,$n)] {
			set newname $rename_by_number($line_type,$n)
		} else {
			set may_be [subst $line_type]($line_name)
			if [info exists $may_be] {
				set newname [set $may_be]
			} else {
			# check for something like ${fam}.
			set fam_len [string length [lindex $txt $i_fam]]
			set line_name [string replace $line_name $fam_len $fam_len]
			set may_be [subst $line_type]($line_name)
			if [info exists $may_be] {
				set newname [set $may_be]
			}
			}
		}
		if [llength $newname] {
			set txt [lreplace $txt $i_name $i_name $newname]
			set part $ren_part($newname)
			set txt [lreplace $txt $i_fam $i_fam $part]
		puts $fout $txt			 		 
			set do_copy_string 1
		} else {
			if {$delete_others == "1"} {
				set do_copy_string 0
			} else {
				puts $fout $txt
			}
		}			 		 
	}
	close $fin
	close $fout
	return $fileout
}
#================================
proc ic_geo_file_rename_fams {filein fileout delete_others type part} {
	if ![file exists $filein] return
	file delete -force $fileout
	set fin [open $filein "r"]
	set fout [open $fileout "w"]
	set do_copy_string 1
	while {  [gets $fin txt ] >= 0 } {
		set word [lindex $txt 0]
		if {[string range $word 0 5] != "define"} {
			if $do_copy_string {
				puts $fout $txt
			}
			continue
		}
		if {$word == "define_model"} {
			set do_copy_string 1
			puts $fout $txt
			continue
		}						
		set line_type [string range $word 7 end]
		if {$type == $line_type} {
			set i_fam [lsearch $txt "family"]
			incr i_fam
			set txt [lreplace $txt $i_fam $i_fam $part]
			puts $fout $txt				
			set do_copy_string 1
		} else {
			if {$delete_others == "1"} {
				set do_copy_string 0
			} else {
				puts $fout $txt
			}
		}			 		 
	}
	close $fin
	close $fout
	return $fileout
}
#=================================================
## Get the curve data at a parameter
## <table border>
## <tr><td><b>par   </b></td><td> curve parameter	</td></tr>
## <tr><td><b>crv   </b></td><td> curve name	</td></tr>
## <tr><td><b>return</b></td><td> list of 4 triplets: 
##       location, and 3 normalized vectors: 
##       tangent, direction to curve center, direction normal to curve plane</td></tr>
## <tr><td><b>return</b></td><td> empty line if curve does not exist. 
## </table>
##
## <b>Notes:</b>
## <ul>
## <li> For faceted curves result is parametrical approximation of the curve 
## <li> Direction to center and normal plane may be {0 0 0} if not defined, e.g. for line 
## </ul>

proc ic_geo_get_crv_data_at_par {par crv} {
    return [vic_geo_info  at_param curve $crv $par]
}

#
# These ic_pre_ procedures do not change geometry!
# Purpose: preview and data settings: 
# 
# Contents:
# ic_pre_project_nearest_srfs
# ic_pre_project_tangent_srfs 
# ic_pre_get_crv_dir
#
set dont_record_command(ic_pre_project_nearest_srfs) 1
set dont_record_command(ic_pre_project_tangent_srfs) 1
set dont_record_command(ic_pre_get_crv_dir) 1
proc ic_pre_project_nearest_srfs {how srfs coords restrict_dir angle {return_what 0}} {
#
# how - "part" then all surfaces of the part, or assume names in srfs
# return empty line or projection of ALL "coords" to nearest "srfs"
# restrict_dir: if angle > 0 then "restrict_dir" must have vector for each
# "coords". Projection direction must have at least "angle" with "restrict_dir".
# Usage: set of locations on curve. restrict_dir is tangency to location
#  return_what = 0 return "max_dist" 
#  return_what = 1 return list of new coords
#                  return "" if cannot project all coords
# Note: Validate input data in advance, outside of the proc:
# This proc moved here because it may be used in loop for numerous sets of surfaces.
# 
	set n 0
	set res ""
	if {$how == "part"} {
		set srfs [ic_geo_get_objects surface $srfs]
	}
	set max_dist 0
	foreach xyz $coords {
		set xyz_proj [ic_geo_project_coords surface $srfs $xyz "0 0 0"]
		if {$xyz_proj == ""} return
		if {$angle > 0} {
			set crv_tan  [lindex $restrict_dir $n]
			set proj_dir [ic_vcalc dir $xyz $xyz_proj]
			set ang [ic_vcalc angle $crv_tan $proj_dir]
			set ang [expr 90.0-abs(90.0-$ang)]
			if {$ang < $angle} return
		}			
		lappend res $xyz_proj
		set d [ic_vcalc distance $xyz $xyz_proj]
		if {$d > $max_dist} {
			set max_dist $d
		}
		incr n
	}
	if {$return_what == 0} {return $max_dist} 
	return $res
}
proc ic_pre_project_tangent_srfs {how srfs coords srf_tan_dir {return_what 0}} {
#
# return empty line or projection of ALL "coords" to "srfs" in "srf_tan_dir".
# "srf_tan_dir" must have vector for each "coords".
# Usage: set of locations on curve. Calculate in advance srf_tan_dir for each coords.
# #  return_what = 0 return "max_dist max_near" 
#  return_what = 1 return list of new coords
#                  return "" if cannot project all coords
# Validate input data in advance, outside of the proc.
# This proc may be used in the loop for numerous sets of surfaces.
#
	set n 0
	set res ""
	if {$how == "part"} {
		set srfs [ic_geo_get_objects surface $srfs]
	}
	set max_dist 0
	set max_near 0
	foreach xyz $coords {
		set dir [lindex $srf_tan_dir $n]
		set mindist 1e9
		set minnear 1e9
		foreach srf $srfs {
			set xyz_p [ic_geo_project_coords surface $srf $xyz $dir]
			set xyz_pmin [ic_geo_project_coords surface $srf $xyz "0 0 0"]
			if {"" == $xyz_p} continue
			set dist [ic_vcalc distance $xyz $xyz_p]
			if {$dist < $mindist} {
				set xyz_proj $xyz_p
				set mindist $dist
			}
			set dist [ic_vcalc distance $xyz $xyz_pmin]
			if {$dist < $minnear} {
				set minnear $dist
			}
		}
		if {$mindist == 1e9} return
		if {$minnear > $max_near} {
			set max_near $minnear
		}
		lappend res $xyz_proj 
		set d [ic_vcalc distance $xyz $xyz_proj]
		if {$d > $max_dist} {set max_dist $d}
		incr n
	}
	if {$return_what == 0} {return "$max_dist $max_near"}
	return $res
}
proc ic_pre_get_crv_dir {crv par_list} {
# using cscale to find direction "tangential" and "out of surface" for single curve.
# assume that surface is relatively "smooth" within the cscale
# 
	set coords ""
	set crv_dirs ""
	set from_dirs ""

	set cscale [expr [ic_geo_crv_length $crv]/100.0]
	set srf [ic_geo_incident curve $crv]
	if {[ic_geo_get_object_type surface $srf] != "param"} return  
	set crv_dirs ""
	foreach par $par_list {
			set xyz [lindex [ic_geo_get_crv_pos $par $crv] 0]
			set tan [ic_geo_get_crv_tan $par $crv]

			set uv [ic_geo_find_nearest_srf_pnt $srf $xyz]
			set nrm [lindex [eval ic_geo_get_srf_nrm $uv $srf] 0]				 
#   calculate (product) normal to curve, tangent to surface
#   this direction is good for "Normal to Curve"
			set dir [ic_vcalc nrm [ic_vcalc product $tan $nrm]]
			set vec [ic_vcalc smult $dir $cscale]
			set p1  [ic_vcalc sum $vec $xyz]
			set p2  [ic_vcalc diff $vec $xyz]
			set p1p [ic_geo_project_coords surface $srf $p1 "0 0 0"]
			set p2p [ic_geo_project_coords surface $srf $p2 "0 0 0"]
			set d1  [ic_vcalc distance $xyz $p1p]
			set d2  [ic_vcalc distance $xyz $p2p]			 
			if {$d1 < $d2} {
				set dir [ic_vcalc smult $dir -1]
			}			
			lappend coords $xyz
			lappend crv_dirs $tan			
			lappend from_dirs $dir
		}
	return [list $coords $crv_dirs $from_dirs]
}

# ic_*image proc use names of MED points or [virtual] vectors as parameters.
#

proc ic_image {{what ""} {name ""} args} { 
	global batch_mode tdv_current_viewer tdv_default arr_args
	global image_min_length
	if $batch_mode return
	set w [vic_viewer]
	if {$w != $tdv_default} {
		vic_eval batch_interp alias $w $w
	}
	set types "arc_im arrow_im line_im point_im text_im csys_im vidpoint_im"
# point.virtual intentionally was not included in types:
# delete with "ic_image delete point.virtual" 
# what 10.0: arc_im, line_im, point_im text_im arrow_im get set delete 
# name   : any name  [or image tag] 
# args : depends
# 
# Usage examples
# ic_image arc window_angle1 "p1 p2 p3"
# ic_image arc window_angle1 "p0 v1 v2 $rad"
# ic_image line w_size "p1 p2"
# ic_image point w_location p1
#
# ic_image get name args
# ic_image set name args
#
# ic_image delete window_angle1 
# ic_image delete arc_im
# ic_image delete all 
#
# Notes:
# 1) args are configure settings, if any, e.g. -color red -size 2 
# 2) if image with name exists, "create" will configure it, not delete or   
# 3) "get" configure value from image is not a public option,
# but may be useful for some med scripts, example: 
# ic_image get arc window_angle1 -radius
#-----------------------------
	set nargs [llength $args]
	set new 0
	# restore current color if something was highlighted
	if {[info exists arr_args(current_image)] && ($what != "highlight") && ($what != "get_name")} {
		ic_image highlight 
	}
	switch $what {
		csys_im {
			# args: name of existing LCS or "all" or "cartesian"! Create LCS with ic_csystem... commands!
			# This function is only image!!! Delete image if LCS does not exist!
			if {$name == "all"} {
				foreach n "global [ic_csystem_list]" {
					ic_image csys_im $n
				}
				return
			}
			if {$name == "cartesian"} {
				foreach n "global [ic_csystem_list]" {
					set type [lindex [ic_csystem_get $n] 0]
					if {$type != "cartesian"} continue
					ic_image csys_im $n
				}
				return
			}
			set data [ic_csystem_get $name]
			set im [$w list_with_tag $name]
			if {$data == ""} {
				if {$im != ""} {$im delete}
				return
			}
			 if {$im == ""} {
				set im [csystem_display -name $name -width 2 -size 100]
				$w add_image $im
# Always first define "type name tag", then "name tag" -> see get_name
				$w add_tag $im csys_im
				$w add_tag $im $name
				set new 1
			}
			$im configure -type [lindex $data 0] \
				-origin [lindex $data 1] -axis0 [lindex $data 2] \
				-axis1 [lindex $data 3] -axis2 [lindex $data 4]
		}
		text_im {
			# args: vector text
			set arr_args(ob) [$w list_with_tag $name]
			set xyz [ic_vset -vec - [lindex $args 0]]
			set txt [lindex $args 1]
			if {($xyz == "") || ($txt == "")} {
#				ic_mess "format: ic_image text_im name xyz text\n"
				return
			}
			if {$arr_args(ob) == ""} {
				set arr_args(ob) [tdv_text_im]
				if {$name == ""} {set name $arr_args(ob)}
				set msg [$w add_image $arr_args(ob)]
				set msg [$w add_tag $arr_args(ob) text_im]
				$w add_tag $arr_args(ob) $name
				set new 1
			}			
			$arr_args(ob) configure -point $xyz -text $txt
		}
		point_im {
			# args: vector
			set arr_args(ob) [$w list_with_tag $name]
			set xyz [ic_vset -vec - [lindex $args 0]]
			if {$xyz == ""} {
#				ic_mess "format: ic_image point_im name xyz\n"
				return
			}
			if {$arr_args(ob) == ""} {
				set arr_args(ob) [tdv_point_im -size 3]
				if {$name == ""} {set name $arr_args(ob)}
				set msg [$w add_image $arr_args(ob)]
				set msg [$w add_tag $arr_args(ob) point_im]
				$w add_tag $arr_args(ob) $name
				set new 1
			}			
			$arr_args(ob) configure -point $xyz
		}
		vidpoint_im {
			# args: vector
			set ob [$w list_with_tag $name]
			set xyz [ic_vset -vec - [lindex $args 0]]
			if {$xyz == ""} {
#				ic_mess "format: ic_image point_im name xyz\n"
				return
			}
			if {$ob == ""} {
				set ob [tdv_point_im -size 3 -color #ff00dd]
				if {$name == ""} {set name $ob}
				set msg [$w add_image $ob]
				set msg [$w add_tag $ob vidpoint_im]	
				$w add_tag $ob $name
				set show_name [ic_gui_set vid_options(show_name)]
				if $show_name {
					set obname [tdv_text_im -text $name -point $xyz -color #ff00dd]
					$w add_image $obname
					$w add_tag $obname vidpoint_name
					$w add_tag $obname $name.name 
				}
				set new 1
			}		
			$ob configure -point $xyz
		}		
		line_im {
			# args: vector vector
			set arr_args(ob) [$w list_with_tag $name]
			if {$name == ""} {set name $arr_args(ob)}
			set xyz1 [ic_vset -vec - [lindex $args 0]]
			set xyz2 [ic_vset -vec - [lindex $args 1]]
			if {($xyz1 == "") || ($xyz2 == "")} {
#				ic_mess "format: ic_image line_im name xyz1 xyz2\n"
				return
			}
			if {$arr_args(ob) == ""} {
				set arr_args(ob) [tdv_line_im -width 2]
				$w add_image $arr_args(ob)
				$w add_tag $arr_args(ob) line_im
				$w add_tag $arr_args(ob) $name
				set new 1
			}
			$arr_args(ob) configure -point1 $xyz1 -point2 $xyz2
		}
		arc_im {
			# args: vector vector vector
			# or  : vector vector vector variable 
			set arr_args(ob) [$w list_with_tag $name]
			set xyz1 [ic_vset -vec - [lindex $args 0]]
			set xyz2 [ic_vset -vec - [lindex $args 1]]
			set xyz3 [ic_vset -vec - [lindex $args 2]]
			if {($xyz1 == "") || ($xyz2 == "") || ($xyz3 == "")} {
#				ic_mess "format(*): ic_image arc_im name xyz1 xyz2 xyz3\n"
				return
			}
			# radius?
			set rad 0
			if {$nargs == 4} {
				set rad [ic_vset - [lindex $args 3]]
			}
			if {$rad == ""} {set rad 0}
			if {$arr_args(ob) == ""} {
				set arr_args(ob) [tdv_arc_im -width 2]
				if {$name == ""} {set name $arr_args(ob)}
				$w add_image $arr_args(ob)
				$w add_tag $arr_args(ob) arc_im
				$w add_tag $arr_args(ob) $name
				set new 1
			}
			if !$rad {
				$arr_args(ob) configure -point1 $xyz1 -point $xyz2 -point2 $xyz3 -radius 0
			} else {
				$arr_args(ob) configure -center $xyz1 -dir1 $xyz2 -dir2 $xyz3 -radius $rad
			}
		}
		arrow_im {
			# args: vector vector
			set ob [$w list_with_tag $name]
			# only one name is legal! name cannot be generic tag! 
			if {[llength $ob] > 1} return
			set xyz1 [ic_vset -vec - [lindex $args 0]]
			set xyz2 [ic_vset -vec - [lindex $args 1]]
			if {($xyz1 == "") || ($xyz2 == "")} return
			set scale [ic_vcalc distance $xyz1 $xyz2]
			if {$ob == ""} {
				set ob [tdv_poly_line_im -draw_dots 0 -width 2]
				set arr_args(ob) $ob
				set image_min_length($ob) $scale
				if {$name == ""} {set name $ob}				
				$w add_image $ob
				$w add_tag $ob arrow_im
				$w add_tag $ob $name
				set new 1
			}
			set pv [$w screen2world  0 0]
			set vnorm [lindex $pv 1]
			set dir   [ic_vcalc dir  $xyz1 $xyz2]
			set s02 [expr $scale*0.1]
			set s07 [expr $scale*0.7]	 
			set vec [ic_vcalc smult $dir $scale] 
			set vec07 [ic_vcalc smult $dir $s07]
# using xyz vec and vnorm set 3 points in screen plane 
			set vscr [ic_vcalc nrm [ic_vcalc product $vec $vnorm]]
			set vscr [ic_vcalc smult $vscr $s02]
			set phead [ic_vcalc sum $xyz1 $vec07]
			set phead1 [ic_vcalc sum $phead $vscr]
			set phead2 [ic_vcalc diff $vscr $phead]
			$ob clear_points
			foreach p "xyz1 xyz2 phead1 phead2 xyz2" {
				$ob add_point [set $p]
			} 
		}
		object {
			set res ""
			foreach ob $name {
				if [catch "$w list_image_tags $ob" tags] continue
				if [catch "$ob get -type" type] continue
				foreach tag $tags {
					if {$tag == $type} continue
					lappend res $tag
					break
				}
			}
			return $res
		}
		get {
			set objs [$w list_with_tag $name]
			if  {$objs == ""} return
			set res ""
			foreach arr_args(ob) $objs {
				set tags [$w list_image_tags $arr_args(ob)]
				foreach tag $tags {
					if {$tag == $name} continue
					lappend res $tag
					foreach a $args {
						if [catch "$arr_args(ob) configure $a" msg] continue
						lappend res $a [lindex $msg end]
					}
				}
			}
			return $res
		}
		set {
			# for one
			set arr_args(ob) [$w list_with_tag $name]
			if  {$arr_args(ob) == ""} return
			foreach ob $arr_args(ob) {
				if [catch "$ob configure $args" msg] continue
			}
			return $msg				
		}
		min_length {

			set how [lindex $args 0]
			# array has "actual" length
			if {$how == "init"} {
				if [array exists image_min_length] {unset image_min_length}
			} else {
				if ![array exists image_min_length] {return 0}
			}
			# set min length for line_im or arrow_im
			# format ic_image min_length arrow_im init||set value 
			if {[lsearch "line_im arrow_im" $name] < 0} {return 0} 
			if {[llength $args] < 2} {return 0}
			set c [lindex $args 1]
			if {[llength $c] != 1} {return 0}
			if [catch "format %g $c"] {return 0}			
			set arr_args(ob) [$w list_with_tag $name]
			if  {$arr_args(ob) == ""} {return 0}
			if {$how == "screen"} {
				set c [$w pixels2dist $c]
			}
			set n 0	
			foreach ob $arr_args(ob) {
				incr n
					if {$name == "line_im"} {
						set p1 [$ob get -point1]
						set p2 [$ob get -point2]
					} else {
						# arrow_im
						set pts [$ob points]
						set p1 [lindex $pts 0]
						set p2 [lindex $pts 1]
					}
					set dir [ic_vcalc dir $p1 $p2]
					set t image_min_length($ob)
					if {[lindex $args 0] == "init"} {
						set $t [ic_vcalc distance $p1 $p2]
						continue
					} 
					if [info exists $t] {
						set was [set $t]
						if {[lindex $args 0] == "reset"} {
							set length $was
						} else {
						# may be reset only if {$was < $c}
							set length $c
						}
						set vec  [ic_vcalc smult $dir $length]
						set p2  [ic_vcalc sum $p1 $vec]
						set tags [$w list_image_tags $ob]
						set thename [lindex $tags 1]
						ic_image $name $thename $p1 $p2
					}			
			}
		return	$n			
		}
		delete {
			if {($name == "") || ($name == "all")} {
				set name ""
				foreach n $types {
					if {$n == "vidpoint_im"} continue
					lappend name $n
				}
				# don't delete vidpoint_im implicitly
			}
			if {$name == "vidpoint_im"} {
				lappend name vidpoint_name
			}
			foreach tag $name {	
				set objs [$w list_with_tag $tag]
				foreach ob $objs {
					$ob delete
					if [info exists image_min_length($ob)] {unset image_min_length($ob)}
					incr new
				}
			}
		}
		get_name {
		# get name for object (actually tag)
			set tags [$w list_image_tags $name]
			return [lindex $tags 1]
		}
		highlight {
		# this option change color of "current" image, basically for onMotion
		# "current" image color will be restored if name was changed or mode was changed 
		if [info exists arr_args(current_image)] {
			set c_name $arr_args(current_image)
			if {$name == $c_name} return
			set c_color $arr_args(current_image_color)
			set obj [$w list_with_tag $c_name]
			if {$obj != ""} {
				$obj configure -color $c_color
			}
			if {$name == ""} {
				unset arr_args(current_image)
				return
			} 
		}  				
		if {$name == ""} return
		set obj [$w list_with_tag $name]
		if {$obj == ""} return
		set c_color [lindex [$obj configure -color] end]
		set arr_args(current_image_color) $c_color
		set arr_args(current_image) $name
		set c_color yellow
		$obj configure -color $c_color	 						 
		}
		default {return $types}
	}
	return $new
}
# ic_image does not create geometry entity:
set dont_record_command(ic_image) 1
proc ic_move_geometry_match_crv_end_pts {crv pts {entities ""}} {
#
# This function scale and rotate curve (and optional entities) 
# so, that curve ends will "match" two points.
# (entities may include the curve, it will not change result of function)
# 
	set p0 [ic_vset -vec - [lindex $pts 0]]
	set p1 [ic_vset -vec - [lindex $pts 1]]
	if {[ic_geo_check_objects_exist curve $crv] == ""} return
	set p0p1 [ic_vcalc diff $p0 $p1]
	if {$p0p1 == ""} return
#
	set types "point curve surface body"
	foreach ent $entities {
		foreach {type name} $ent ""
		set tmp_$type($name) 1		
	}
	set tmp_curve($crv) 1
	foreach type $types {
		if [info exists tmp_$type] {
			set ${type}_names [array names tmp_$type]
	} else {	
			set ${type}_names ""
		}			 
	}
# scale first 
	set c0 [lindex [ic_geo_get_crv_pos 0 $crv] 0]
	set c1 [lindex [ic_geo_get_crv_pos 1 $crv] 0]
	set c0c1  [ic_vcalc diff $c0 $c1]
	set len_p [ic_vcalc len $p0p1]
	set len_c [ic_vcalc len $c0c1]
	if {$len_p != 0 && $len_c != 0 && $len_p != $len_c} {
		set scale [expr $len_p/$len_c]
		foreach type $types {
			if {[set ${type}_names] == ""} continue	
			ic_move_geometry $type names [set ${type}_names] scale $scale
		}
	}
#
# translate
	set c0 [lindex [ic_geo_get_crv_pos 0 $crv] 0]
	set translate [ic_vcalc diff $c0 $p0]
	foreach type $types {
		if {[set ${type}_names] == ""} continue	
		ic_move_geometry $type names [set ${type}_names] translate $translate
	}
# 
	if {$len_p == 0 || $len_c == 0} return
#
# rotate
	set c0 [lindex [ic_geo_get_crv_pos 0 $crv] 0]
	set c1 [lindex [ic_geo_get_crv_pos 1 $crv] 0]
	set c0c1  [ic_vcalc diff $c0 $c1]
	set angle [ic_vcalc angle $p0 $p1 $c1]
	if {$angle == 180} {
		set axis [ic_vcalc vec_norm $p0p1]
	} else {
		set axis [ic_vcalc product $p0p1 $c0c1]
	}		  
	if {$angle != 0} {
		set angle [expr -($angle)]
		foreach type $types {
			if {[set ${type}_names] == ""} continue	
			ic_move_geometry $type names [set ${type}_names] rotate $angle cent $p0 rotate_axis $axis
		}
	}		
#
}

proc ic_geo_move_lcs_lcs {from to type names} {
# current LCS not used
# from may be LCS name or 3 points/locations

	set nfrom [llength $from]
	set nto [llength $to]

	if {($nfrom != 1 && $nfrom != 3) || ($nto != 1 && $nto != 3)} {
		ic_mess "Error: LCS -> LCS incorrect data\n" red
		return
	}
	
	set was_csys [ic_csystem_get_current]
	ic_csystem_set_current global
	
	if {$nfrom == 1} {
		if {[ic_csystem_get $from] == ""} {
			ic_mess "Error: not defined LCS \"from\"\n" red
			ic_csystem_set_current $was_csys
			return
		}
		if {[ic_csystem_get $from] != ""} {
		    set p00 [ic_coords_into_global "0 0 0" $from]
		    set p01 [ic_coords_into_global "1 0 0" $from]
		    set p02 [ic_coords_into_global "0 1 0" $from]
        }
	} else {
		foreach "p00 p01 p02" $from ""
		set i 0
		foreach p $from {
            if {[llength [ic_geo_check_objects_exist point $p]]} {
                if {[ic_csystem_get_current] != "global"} {
                    set p0$i [ic_coords_into_global [ic_geo_get_point_location $p]]
                } else {
                    set p0$i [ic_geo_get_point_location $p]
                }
            }
            incr i
        }
	}

	if {$nto == 1} {
		if {[ic_csystem_get $to] == ""} {
			ic_mess "Error: not defined LCS \"to\"\n" red
			ic_csystem_set_current $was_csys
			return
		}
		if {[ic_csystem_get $to] != ""} {
		    set p10 [ic_coords_into_global "0 0 0" $to]
		    set p11 [ic_coords_into_global "1 0 0" $to]
		    set p12 [ic_coords_into_global "0 1 0" $to]
		}
	} else {
		foreach "p10 p11 p12" $to ""
		set i 10
		foreach p $to {
            if {[llength [ic_geo_check_objects_exist point $p]]} {
                if {[ic_csystem_get_current] != "global"} {
                    set p$i [ic_coords_into_global [ic_geo_get_point_location $p]]
                } else {
                    set p$i [ic_geo_get_point_location $p]
                }
            }
            incr i
        }
	}
	
	# translate to origin
	set translate1 [ic_vcalc diff $p00 {0 0 0}]
	set translate2 $p10
	ic_move_geometry $type names $names translate $translate1

	set p01 [ic_vcalc diff $p00 $p01]
	set p02 [ic_vcalc diff $p00 $p02]
	set p00 "0 0 0"
	set p11 [ic_vcalc diff $p10 $p11]
	set p12 [ic_vcalc diff $p10 $p12]
	set p10 "0 0 0"

	set vecx1 [ic_vcalc dir $p00 $p01]
	set vecx2 [ic_vcalc dir $p10 $p11]
	set vecy1 [ic_vcalc dir $p00 $p02]
	set vecy2 [ic_vcalc dir $p10 $p12]
	# check that points are not on the line.
	set v [ic_vcalc product $vecx1 $vecy1]
	set vv [ic_vcalc product $vecx2 $vecy2]
	if {$v == "" || $vv == ""} {
		ic_mess "data error in ic_geo_move_lcs_lcs_2\n" red
		return
	}

	set axs1 [ic_vcalc product $vecx2 $vecx1]
	# may be collinear, but opposite
	if {$axs1 == "0 0 0"} {
		# use any normal vector
		set axs1 [ic_vcalc vec_norm $vecx1]
	}
	set ang1 [ic_vcalc angle_normal $axs1 $vecx1 $vecx2]
	set xyz [ic_vcalc sum $p10 $vecy1]
	ic_delete_geometry point names tmp_pnt 0
	ic_geo_cre_pnt TMP tmp_pnt $xyz
	if {$ang1 != 0} {
		ic_move_geometry point names tmp_pnt rotate $ang1 cent $p10 rotate_axis $axs1
	}
	set xyz [ic_geo_get_point_location tmp_pnt]
	set vecx [ic_vcalc dir $p10 $xyz]
	set ang2 [ic_vcalc angle_normal $vecx2 $vecx $vecy2]
	ic_delete_geometry point names tmp_pnt 0
	if {$ang1 != 0} {
		ic_move_geometry $type names $names rotate $ang1 cent $p10 rotate_axis $axs1
	}
	if {$ang2 != 0} {
		ic_move_geometry $type names $names rotate $ang2 cent $p10 rotate_axis $vecx2
	}
	# translate from origin to target
	ic_move_geometry $type names $names translate $translate2

	ic_csystem_set_current $was_csys
}

proc ic_is_triplet {xyz} {
	if {[llength $xyz] != 3} {return 0}
	foreach c $xyz {
		if {[llength $c] != 1} {return 0}
		if [catch "format %g $c"] {return 0}
	}
	return 1
}
set dont_record_command(ic_is_triplet) 1
proc ic_geo_mesh_srf_angle {srf args} {
    global uns_cur projlib
# calculate mesh surface "curvature angle" & average normal   
# Note: return angle is 2*max_angle from average normal
# NURB curvature has different definition. Results are close only for smooth surfaces.
# Note: elements must be connected; otherwice result may be wrong. 
# 
	  set obj [$projlib get_object_by_name surface $srf]
	  if {$obj == ""} {return 0}
	  if {[$obj get_object_type] != "mesh"} {return 0}
   ic_undo_suspend
	  set num [$projlib get_number_by_name surface $srf]
    set uns_cur [uns_mesh]
    $uns_cur initialize_mesh
	$uns_cur create_family F_TMP -1
	set map [uns_map -mesh $uns_cur -name Tmp -list_type 2]
	$projlib finish_surface_edit $num
	eval $uns_cur import_tris F_TMP [$projlib export_tris $num]
    $map add_all_type TRI_3
	ic_uns_reorient Tmp consistent Tmp
	set av_norm [$map element_normal]
	set max_angle [expr [lindex $av_norm 1]*2]		 
		    $map delete
		    $uns_cur delete
			set uns_cur ""
   ic_undo_resume
	return [list $max_angle [lindex $av_norm 0]]
}
set dont_record_command(ic_geo_mesh_srf_angle) 1
#-----------------------------------------
proc ic_vdefine_calculate {name method data} {
# Purpose:set parameter / vector value from calculations [geometry]
#       (combine ic_vdefine with geometry data)
# Return: parameter/vector "NAME = VALUE" or empty line if not able calculate and define
# Examples:
# ic_vdefine_calculate par1 curve_length CURVE.0
# ic_vdefine_calculate par2 point_on_curve "POINT.1 CURVE.2 xmin"
#
# Notes:
# 1. Function is vital for replay file parametrical definitions
# 2. Function set parameter/vector as a constant. In order to change parameter,
#   (e.g. after curve length was changed) it's necessary to call ic_vdefine... again.
# 3. Type of result [variable or vector] will be defined based on "method" 
	
switch $method {
	curve_length {
		#format: curve
		set err [catch "ic_geo_crv_length $data" par]
		if $err return
		set type variable
	}	 
	point_on_curve {
		#format: point curve how, where how = xmax/ymax...
		# kind of opposite of "ic_point crvend_par curve how param"
		foreach "pnt crv how" $data ""
		set err [catch "ic_geo_find_nearest_crv_pnt $crv $pnt" par]
		if $err return
		set xyz [ic_geo_get_crv_end_generic $crv $how]
		set endpar [ic_geo_find_nearest_crv_pnt $crv $xyz]
		if {$endpar > 0.5} {
			set par [expr 1-$par]
		}
		set type variable
	}
	vector_length {
		#format point/vector/triplet/vector-expr length/scalar-expr
		# define vector from direction and length
		foreach "pnt len" $data ""
		set xyz [ic_vset -vec - $pnt]
		set len [ic_vset - $len]
		if {$xyz == "" || $len == ""} return
		set par [ic_vcalc nrm_smult $xyz $len] 
		set type vector
	}
	vector_project_axis {
		#format point/vector/triplet/vector-expr axis, where axis = 0|1|2
		foreach "pnt axis" $data ""
		set xyz [ic_vset -vec - $pnt]
		if {$xyz == ""} return
		set par [lreplace $xyz $axis $axis 0]
		set type vector
	}
	vector_scale {
		#format point/vector/triplet/vector-expr scale
		foreach "pnt scale" $data ""
		set xyz [ic_vset -vec - $pnt]
		set scale [ic_vset - $scale]
		if {$xyz == "" || $scale == ""} return
		set par [ic_vcalc smult $xyz $scale]
		set type vector
	}
	angle {
		#format point/vector/triplet/vector-expr point/...
		foreach "p1 p2" $data ""
		set v1 [ic_vset -vec - $p1]
		set v2 [ic_vset -vec - $p2]
		if {$v1 == "" || $v2 == ""} return
		set par [ic_vcalc angle $v1 $v2]
		set type variable
	}
	default return
}
	ic_vdefine $name $type $par
	return "$name = $par" 
}
#
#
#
proc ic_vid_object {mac {P PART1} {N 1} args} {
	# object must have next procedures:
	# mac.init {P N Ref ...} - create object and define control vectors/parameters; 
	# mac.delete {P N} - delete geometry and definitions
	# mac {P N} - create object with current definitions
	# mac.anyname - any optional proc
	# global variable "_" is namespace abbreviation for current/last $mac.$P.$N 
	# ic_vid_object may create set of variables, which will be not shown in replay file
	# Otherwice during replay they will be defined twice.
#
#
	set obname $mac.$P.$N
#********************************************************
# Delete old entity with the same name or create new name? 
	global vid_options   
    if ![ic_gui_set vid_options(replace)] {
		
		set obname [ic_vset -new_name $obname all]
	}
#******************************************************
#
#
	foreach "ob sub" [split $mac .] ""
	set ::vid_templates::_ $obname
	# First word of args (if any) must be name of reference point
	
	# part method must be defined. it may be done on GUI initialization
	if {[ic_vset -method object part] == ""} {
		ic_vdefine part method object names
	}
	# may be "name" or "name.delete" with args:
	
	if {$sub == "delete"} {
		eval ::vid_templates::$mac $P $N $args
		ic_mess "Done: deleted $mac $P $N\n"	 
		return
	}
	
	if {$args == ""} {
		# currently only "create current object" function may be without args.
		if {[ic_vset -type $ob.$P.$N] != "object"} {
			ic_mess "Object not defined\n" red
			return
		} 
		# delete geometry before create new instance
		catch "eval ::vid_templates::$ob.delete $P $N" 
		eval ::vid_templates::$mac $P $N
		return
	} 
		set Ref [lindex $args 0]
		set Ref_exists 1 
		if {[ic_vset -type $Ref] != "vector" && [ic_vset -med_entity $Ref] != "point"} {
			set Ref_exists 0	
		}
		#Validate Ref point (or expression): 
		set xyz [ic_vset -vec - $Ref]
		if {$xyz == ""} {
			ic_mess "Cannot calculate Ref location\n" red
			return
		}
		if !$Ref_exists {
		# define variable for Ref point and set old definition which may include variables:
			set oldRef $Ref
			set Ref $ob.$P.$N.Ref 
			ic_vdefine $Ref vector $oldRef
		}
		# if there are parameters and name has no "." call $mac.init
		# Rule #1: objects controls by virtual variables.
		# Rule #2: objects (recreate) functions have only P, N parameters, no args 
		if {$ob == $mac} {
			set mac $mac.init
		}
		eval ::vid_templates::$mac $P $N $Ref [lrange $args 1 end]
}
namespace eval vid_templates {
	proc macro {P N} {
		set objs [ic_vset -def macro.$P.$N]
		if {$objs == ""} return
		foreach name $objs { 
			set def  [ic_vset -def $name]
			set method [ic_vset -method $name]
			switch [ic_vset -type $name] {
				curve {ic_curve $method $P $name $def}
				surface {ic_surface $method $P $name $def}
			}
		}
	}
	proc line {P N Ref p1 p2} {
		ic_curve point $P $P.$N.1 [list $p1 $p2] cre_pnts 0
	} 
	proc box0 {P N Ref W L H} {
		# P - part
		# N - number or name of box
		# Ref - ref point / ViPoint name
		# W,L,H - size /scalar-expressions
		# No virtual definitions at all. Pure replay.
	ic_surface 4pts $P $P.$N.1 [list $Ref $Ref+vector($L,0,0) $Ref+vector(0,$W,0) $Ref+vector($L,$W,0)]
	ic_surface 4pts $P $P.$N.2 [list $Ref+vector(0,0,$H) $Ref+vector($L,0,$H) $Ref+vector(0,$W,$H) $Ref+vector($L,$W,$H)]
	ic_surface 4pts $P $P.$N.3 [list $Ref+vector(0,0,0) $Ref+vector($L,0,0) $Ref+vector(0,0,$H) $Ref+vector($L,0,$H)]
	ic_surface 4pts $P $P.$N.4 [list $Ref+vector(0,$W,0) $Ref+vector($L,$W,0) $Ref+vector(0,$W,$H) $Ref+vector($L,$W,$H)]
	ic_surface 4pts $P $P.$N.5 [list $Ref+vector(0,0,0) $Ref+vector(0,$W,0) $Ref+vector(0,0,$H) $Ref+vector(0,$W,$H)]
	ic_surface 4pts $P $P.$N.6 [list $Ref+vector($L,0,0) $Ref+vector($L,$W,0) $Ref+vector($L,0,$H) $Ref+vector($L,$W,$H)]
	return $P.$N
	} 
	
	proc box8.initxyz {P N Ref {W 1} {L 1} {H 1}} {
		# P - part
		# N - number or name of box
		# Ref - ref point / ViPoint name
		# W,L,H - size /scalar-expressions
		# create ViPoints for corners as constant vectors (no parameters)
		# create object in order to find/recreate box from points
		# must set new points coordinates (screen or explicitly change definition)
	# one-time definitions:
#
# 	next is a list of virtual "parameters". If "parameter" was changed, object must be rebuild. Delete if no object.
		ic_vdefine box8.$P.$N object "$P.$N.P1 $P.$N.P2 $P.$N.P3 $P.$N.P4 $P.$N.P5 $P.$N.P6 $P.$N.P7 $P.$N.P8" 
#   next are names of geometry objects. Delete if object was deleted. 
		ic_vdefine box8.$P.$N.surface object "$P.$N.S1 $P.$N.S2 $P.$N.S3 $P.$N.S4 $P.$N.S5 $P.$N.S6"
		
		ic_vdefine $P.$N.P1 vector [ic_vset -vec - $Ref] 
		ic_vdefine $P.$N.P2 vector [ic_vset -vec - $Ref+vector($L,0,0)] 
		ic_vdefine $P.$N.P3 vector [ic_vset -vec - $Ref+vector($L,$W,0)]
		ic_vdefine $P.$N.P4 vector [ic_vset -vec - $Ref+vector(0,$W,0)]	 
				
		ic_vdefine $P.$N.P5 vector [ic_vset -vec - $Ref+vector(0,0,$H)] 		
		ic_vdefine $P.$N.P6 vector [ic_vset -vec - $Ref+vector($L,0,$H)]
		ic_vdefine $P.$N.P7 vector [ic_vset -vec - $Ref+vector(0,$W,$H)]		
		ic_vdefine $P.$N.P8 vector [ic_vset -vec - $Ref+vector($L,$W,$H)]
		
		box8 $P $N
		return "$Ref box8 $L $W $H"	
	}
	proc box8 {P N} {
	variable _
	# create box
	set 6_srfs {{4 3 2 1} {5 6 7 8} {1 2 6 5} {3 4 8 7} {5 8 4 1} {2 3 7 6}}
	set nsrf 1
	set res ""
	foreach pnums $6_srfs {
		set pts ""
		foreach p $pnums {lappend pts $_.P$p}
		set ask_name ${_}.S$nsrf
		set srf_name [ic_surface 4pts $P $ask_name $pts btopo 0]
		if {$srf_name == ""} return
		lappend res $srf_name
		incr nsrf
	}
	if [ic_gui_set vid_options(new_srf_topo)] {
		ic_build_topo 0.001 -surfaces $res
	}
	return $_
	} 

	proc box8.delete {P N {what geom}} {
	# delete all or delete geometry only and keep definitions [default]
	variable _
		if {$what == "all"} {
			# delete virtual definitions 
			foreach vp [ic_vset -def $_] {
				ic_vset -delete $vp
			}
			# delete list of virtual definitions	
			ic_vset -delete $_	
		}
		# delete current geometry
		ic_delete_geometry surface names "$_.S1 $_.S2 $_.S3 $_.S4 $_.S5 $_.S6" 0 
	}
		
	proc box8.init {P N Ref {L 1} {W 1} {H 1}} {
	variable _
		# P - part
		# N - number or name of box
		# Ref - ref point / ViPoint name
		# L,W,H - parameters or scalar-expressions.
		# create ViPoints for corners as variable vectors. Use parameters for L W H
		# create object in order to find/recreate box from points
		# Mod: June 30 2006: If not vid_mode return if object is not fully defined now.
	if ![ic_gui_set vid_options(vid_mode)] {
		foreach v "$W $L $H" {
			set vc [ic_vset - $v]
			if {$vc == "" || $vc == 0} {
				status_message "Can't create box with side $v" red
				return
			}
		}
	}
		ic_vdefine $_.W variable $W
		ic_vdefine $_.L variable $L
		ic_vdefine $_.H variable $H

		ic_vdefine $_.P1 vector $Ref 
		ic_vdefine $_.P2 vector $Ref+vector($_.L,0,0) 
		ic_vdefine $_.P3 vector $Ref+vector($_.L,$_.W,0)	
		ic_vdefine $_.P4 vector $Ref+vector(0,$_.W,0)	 
			
		ic_vdefine $_.P5 vector $Ref+vector(0,0,$_.H) 		
		ic_vdefine $_.P6 vector $Ref+vector($_.L,0,$_.H)
		ic_vdefine $_.P7 vector $Ref+vector($_.L,$_.W,$_.H)	
		ic_vdefine $_.P8 vector $Ref+vector(0,$_.W,$_.H)		
		
		
		# list of all "this object" definitions
		ic_vdefine $_ object part "$_.L $_.W $_.H $_.P1 $_.P2 $_.P3 $_.P4 $_.P5 $_.P6 $_.P7 $_.P8"	
		set res [box8 $P $N]
		if {$res == ""} {status_message "Cannot create box. Check input and try again" red} 
	} 
	proc body_1 {P N} {
			# Using abbreviations for readability:
		foreach {n d} "Axis $P.$N.Axis L $P.$N.L R $P.$N.R D $P.$N.D Axis2 $P.$N.Axis2 L2 $P.$N.L2 R2 $P.$N.R2" {
			set $n $d
		}
	
		ic_curve arc_ctr_rad $P $P.$N.C1 "$P.$N.P1 $P.$N.X $P.$N.Axis $P.$N.R 0 180" 
		ic_curve arc_ctr_rad $P $P.$N.C2 "$P.$N.P2 $P.$N.X $P.$N.Axis $P.$N.R 0 180" 
		ic_curve arc_ctr_rad $P $P.$N.CD1 "$P.$N.PD1 $P.$N.Axis $P.$N.Axis2 $P.$N.R2 0 360" 
		ic_curve arc_ctr_rad $P $P.$N.CD2 "$P.$N.PD2 $P.$N.Axis $P.$N.Axis2 $P.$N.R2 0 360"	
		ic_surface curves $P $P.$N.S1 "$P.$N.C1 $P.$N.C2" 
#		ic_surface curves $P $P.$N.S2 "$P.$N.CD1 $P.$N.CD2" 
#		ic_surface curves $P $P.$N.S3 "$P.$N.C1" 
#		ic_surface curves $P $P.$N.S4 "$P.$N.C2" 
		ic_surface curves $P $P.$N.S5 "$P.$N.CD1" 
		ic_surface curves $P $P.$N.S6 "$P.$N.CD2"					
		return "$P $N"
	}
	proc body_1.init {P N Ref {Axis "1 0 0"} {L 5} {R 2} {D 2} {Axis2 "1 0 2"} {L2 5} {R2 1}} {
		# formal variables definition: actual initial data may be not variable  
		# Don't include Ref point - it will be defined automatically if necessary.
			# Axis: Direction of main [half-]cylinder axis (from Ref point)   
		ic_vdefine $P.$N.Axis vector $Axis
			# L : main cylinder length
		ic_vdefine $P.$N.L variable $L
			# R	: main cylinder radius	
		ic_vdefine $P.$N.R variable $R
			# D : distance along main axis to axis of second half-cylinder. 
			# Intentionally no constraints on D, so it me be outside of main   	
		ic_vdefine $P.$N.D variable $D
			# Axis2: Direction of 2-nd cylinder axis (from 2-nd cyl point)   
		ic_vdefine $P.$N.Axis2 vector $Axis2		
			# L2 : 2-nd cylinder length	
		ic_vdefine $P.$N.L2 variable $L2
			# R2 : 2-nd cylinder radius				
		ic_vdefine $P.$N.R2 variable $R2


		# body_1.$P.$N part object has list of above "dependents" which may change object	 
		ic_vdefine body_1.$P.$N object part "$P.$N.Axis $P.$N.L $P.$N.R $P.$N.D $P.$N.Axis2 $P.$N.L2 $P.$N.R2"
		
		# few more definitions;
		ic_vdefine $P.$N.P1 vector $Ref 		
		ic_vdefine $P.$N.P2 vector $Ref+$P.$N.Axis*$P.$N.L
		ic_vdefine v.$P.$N.X vector $P.$N.Axis*$P.$N.Axis2
		ic_vdefine $P.$N.PD1 vector $Ref+$P.$N.Axis*$P.$N.D		
		ic_vdefine $P.$N.PD2 vector $P.$N.PD1+$P.$N.Axis2*$P.$N.L2
		
		return [body_1 $P $N]
	}
	proc cyl.init  {P N Ref {Pdir "0 1 0"} {Axis "1 0 0"} {L 5} {R 2}} {
		ic_vdefine $P.$N.Pdir vector $Pdir
		ic_vdefine $P.$N.Axis vector $Axis		
		ic_vdefine $P.$N.L variable $L
		ic_vdefine $P.$N.R variable $R
		ic_vdefine cylinder.$P.$N object part "$P.$N.Pdir $P.$N.Axis $P.$N.L $P.$N.R $P.$N.Angle"
		
		ic_vdefine $P.$N.P1 vector $Ref+$P.$N.Pdir*$P.$N.R
		return [cyl $P $N] 
	}	
	proc cyl {P N} {
		ic_curve arc_ctr_rad $P $P.$N.C1 "$P.$N.P1 $P.$N.X $P.$N.Axis $P.$N.R 0 180" 
		ic_curve arc_ctr_rad $P $P.$N.C2 "$P.$N.P2 $P.$N.X $P.$N.Axis $P.$N.R 0 180" 
		ic_curve arc_ctr_rad $P $P.$N.CD1 "$P.$N.PD1 $P.$N.Axis $P.$N.Axis2 $P.$N.R2 0 360" 
		ic_curve arc_ctr_rad $P $P.$N.CD2 "$P.$N.PD2 $P.$N.Axis $P.$N.Axis2 $P.$N.R2 0 360"	
		ic_surface curves $P $P.$N.S1 "$P.$N.C1 $P.$N.C2" 
#		ic_surface curves $P $P.$N.S2 "$P.$N.CD1 $P.$N.CD2" 
#		ic_surface curves $P $P.$N.S3 "$P.$N.C1" 
#		ic_surface curves $P $P.$N.S4 "$P.$N.C2" 
		ic_surface curves $P $P.$N.S5 "$P.$N.CD1" 
		ic_surface curves $P $P.$N.S6 "$P.$N.CD2"					
		return "$P $N"
	}	
		 	
}
proc ic_gui_set {name {value ""} {args ""}} {
# Execute "set" [or some window command] in GUI from batch
# Limit usage:
# a) to check/set GUI variables without duplication
# b) check/set some GUI windows 
#
# Usage:
# ic_gui_set $name :Return value if defined. Set return 0 otherwice  
# ic_gui_set $name $value: set "name" "value". 
# ic_gui_set $name $value $args: evaluate in GUI command "$name $value $arg..." 
# Note: It's possible to use command from GUI. No good reasons. 
# Examples:
# ic_gui_set options(editor)
# ic_gui_set auto_pick_mode 0
# ic_gui_set info command .select_opts*frame
#
	if {$args == ""} {
		if {$value == ""} {
			set res [vic_eval set $name]
			if {$res == ""} {
				set res [vic_eval set $name 0]			
			}
		} else {
			if {[llength $value] > 1} {
				set res [vic_eval set $name \{"$value"\}]
			} else {
				set res [vic_eval set $name $value]
			}
		}
		return $res 			
	} 
	
	set res [vic_eval $name $value $args]		
	return $res	
}
set dont_record_command(ic_gui_set) 1

proc ic_gui_set2 {name {value ""}} {
    if {[llength $value] > 1} {
        set res [vic_eval set $name \{"$value"\}]
    } else {
        set res [vic_eval set $name $value]
    }
    return $res
}


#=================================================================	
# Info on virtual design which useful for GUI; dont_record 
set dont_record_command(ic_vid_options) 1

proc ic_vid_options {what {data ""}} {
switch $what {	
 "macro" {
	# data = "names" - return namespace names (cleaned)
	if {$data == "names"} {
		set clist ""
		foreach n [info commands ::vid_templates::*] {
			lappend clist [lindex [split $n ::] end]
		}
		return $clist
	} else {
		if [catch "info args ::vid_templates::$data" par] return
		set res "proc $data \{ $par \} \{\n"
		append res [info body ::vid_templates::$data]
		append res \n\}
		return $res
	}
}

default {
}
}
return 
}
proc ic_regenerate_tris {} {
	global projlib
	if {![ic_geo_num_objects curve] && ![ic_geo_num_objects surface]} {return 0}
	busy 1	
	set n [$projlib regenerate_tris]
	# Must recreate all bsp 
    clear_geom_objects
    define_geom_objects
    busy 0
	return $n
}
proc ic_geo_get_points_near {type name {tol ""}} {
	global projlib
# Create subset of geometry points within tol distance from entity
# May be moved in med
	if [catch {ic_geo_bbox_of_entities [list "$type $name"]} minmax] return
	foreach "min max" $minmax ""
	set list ""
	if {$tol == ""} {set tol [ic_gui_set geo_cad(toler)]}
	foreach "x0 y0 z0" [lindex $minmax 0] ""
	foreach "x1 y1 z1" [lindex $minmax 1] ""
	foreach c "x0 y0 z0" {set $c [expr [set $c]-($tol)]}
	foreach c "x1 y1 z1" {set $c [expr [set $c]+($tol)]}	
	foreach num [$projlib list_objects point] {
		set ploc [$projlib get_point_loc $num] 
		foreach "x y z" $ploc ""
		if {$x < $x0 || $x > $x1} continue
		if {$y < $y0 || $y > $y1} continue		
		if {$z < $z0 || $z > $z1} continue		
		set xyz [ic_geo_project_coords $type $name $ploc]
		if {[ic_vcalc distance $ploc $xyz] > $tol} continue
		lappend list [$projlib get_name point $num]
	}
	return $list 
}
