# Library functions for the scripting library (scrib)
#
# File: scrib/lib/feature.tcl
#
# $Revision$ on $Date$
# 
# Copyright (c) 1993-2003 ICEM CFD Engineering
#
#--------------------------------------------------------------------

## Needs doc

proc scrib_lib_feature_find_opposite_side_old {srf {bnd ""}} {
    
    if {$bnd == ""} {
	set bnd [ic_geo_boundary surface $srf 1 1]
    }
    
    for {set i 0} {$i < [llength $bnd]} {incr i} {
	set j [expr $i+1]
	if {$j == [llength $bnd]} {set j 0}
	set p_l1 [ic_geo_boundary curve [lindex $bnd $i] 0 1]
	set p_l2 [ic_geo_boundary curve [lindex $bnd $j] 0 1]
	foreach p $p_l1 {
	    if {[lsearch $p_l2 $p] != -1} {
		lappend p_nm $p
		lappend p_lc  [ic_geo_get_point_location $p]
	    }
	}
    }
    
    if ![info exists p_nm] {
	busy reset
	return
    }
    
    for {set c 0} {$c < [llength $p_nm]} {incr c} {
	set i [expr $c-1]
	if {$i == -1} {set i "end"}
	set j [expr $c+1]
	if {$j == [llength $p_nm]} {set j 0}
	set v1 [scrib_lib_math_vector_sub [lindex $p_lc $i]  [lindex $p_lc $c]]
	set v2 [scrib_lib_math_vector_sub [lindex $p_lc $j]  [lindex $p_lc $c]]
	set v1_len [scrib_lib_math_vector_length $v1]
	set v2_len [scrib_lib_math_vector_length $v2]
	if {$v1_len == 0 || $v2_len == 0} {
	    return
	} else {
	    set ang_rad [scrib_lib_math_angle_between_vectors $v1 $v2]
	    set ang     [expr $ang_rad/0.017453292]
	}
	lappend p_lst [list [lindex $p_nm $c] $ang]
    }
    
    set p_srt [lsort -real -index 1 $p_lst]
    foreach i "0 1 2 3" {
	lappend c_pl [lsearch $p_nm [lindex [lindex $p_srt $i] 0]]
    }
    
    set c_pl  [lsort -real $c_pl]
    set sd_1  [lrange $bnd [expr [lindex $c_pl 0]+1] [lindex $c_pl 1]]   
    set sd_2  [lrange $bnd [expr [lindex $c_pl 1]+1] [lindex $c_pl 2]]   
    set sd_3  [lrange $bnd [expr [lindex $c_pl 2]+1] [lindex $c_pl 3]] 
    set sd_4a [lrange $bnd [expr [lindex $c_pl 3]+1] end]
    set sd_4b [lrange $bnd 0 [lindex $c_pl 0]]
    set sd_4  [concat $sd_4a $sd_4b]   
    
    return    [list   $sd_1 $sd_2 $sd_3 $sd_4]
}

## Needs doc

proc scrib_lib_feature_find_opposite_side {srf {bnd ""} {ret 0}} {
    return [ic_meshdirect_find_opposite_side $srf $bnd $ret]
}

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-

proc scrib_lib_feature_set_param_crvs_prll {crv_p} {
    ic_meshdirect_set_param_crvs_prll $crv_p
}

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-
proc scrib_lib_feature_set_param_crvs {crv_list msh_lmt} {
    ic_meshdirect_set_param_crvs $crv_list $msh_lmt
}


#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-
#proc scrib_lib_geo_make_conn_loops {curves} {
  
# set j [llength $curves]
# while {$j > 0} {
#  set crv    [lindex $curves 0]
#  set curves [lreplace $curves 0 0]
#  set pnts   [ic_geo_boundary curve $crv]
#  set crvs_0 $crv
#  set crvs_1 ""
#  foreach i "0 1" {
#     set pt   [lindex $pnts $i]
#     while 1 {
#        set chck 0
#        set cr_p [ic_geo_incident point $pt]
#        set plc  [lsearch  $cr_p $crv]
#        set cr_p [lreplace $cr_p $plc $plc]
#        foreach c $cr_p {
#          if {[lsearch $curves $c] == -1} continue
#          if {[lsearch $crvs_0 $c] == -1 && [lsearch $crvs_1 $c] == -1} {
#             lappend crvs_$i $c
#             set plc [lsearch $curves $c]
#             set curves [lreplace $curves $plc $plc]
#             set chck 1
#             }
#          if $chck break
#          }
#        if !$chck break
#        set crv $c
#        set pts [ic_geo_boundary curve $crv]
#        set plc [lsearch $pts $pt]
#        set pt  [lindex [lreplace $pts $plc $plc] 0]
#        }
#     }
#   set j [llength $curves]
#   if {$crvs_1 != ""} {
#      for {set m 0} {$m < [llength $crvs_1]} {incr m} {
#         set crvs_0 [concat [lindex $crvs_1 $m] $crvs_0]
#         }
#      }
#   lappend ret [list $crvs_0]
# }
# return $ret
#}



proc scrib_lib_feature_find_container  {crvs {srfs ""}} {
global mv_set   
   set l_pnts ""
   set l_crvs ""
   foreach cr $crvs {
      set pnts [ic_geo_boundary curve $cr 0 1]    
      foreach p $pnts {
        if {[lsearch $l_pnts $p] == -1} {
             lappend l_pnts $p
             lappend loc_p  [ic_geo_get_point_location $p]
             }
        }
      }
   set len [llength $l_pnts]
   if {$len < 3} return  
   for {set i 0} {$i < $len} {incr i} {
      set j [expr $i+1]
      if {$j == $len} {set j 0}
      set k [expr $j+1]
      if {$k == $len} {set k 0}
      set v1 [scrib_lib_math_vector_sub [lindex $loc_p $i] [lindex $loc_p $j]]
      set v2 [scrib_lib_math_vector_sub [lindex $loc_p $i] [lindex $loc_p $k]]
      if {[scrib_lib_math_angle_between_vectors $v1 $v2] != 0} {
         set p1 [lindex $loc_p $i]
         set p2 [lindex $loc_p $j]
         set p3 [lindex $loc_p $k]
         break
         }
      }
   if ![info exists p1] return
   set xyz [scrib_lib_feature_find_container_math $p1 $p2 $p3]
   if {$srfs == ""} {
      set l_crvs $crvs
      } else {
        foreach sr $srfs {
          set cr [ic_geo_boundary surface $sr 0 1]
          foreach c $cr {
            if {[lsearch $l_crvs $c] == -1} {lappend l_crvs $c}
            }
          }
        }
   if {$l_crvs == ""} return
   set l_pnts ""
   set fam [ic_geo_get_family curve [lindex $l_crvs 0]]
   foreach cr $l_crvs {
     set pnts [ic_geo_boundary curve $cr 0 1]
     foreach p $pnts {
       if {[lsearch $l_pnts $p] == -1} {
          lappend l_loc [ic_geo_get_point_location $p]
          lappend l_pnts $p
          }
       }
     set fam [ic_geo_get_family curve [lindex $l_crvs 0]]
     set mid [ic_geo_cre_pnt_on_crv_at_par $fam "" 0.5 $cr]
     if {$mid != ""} {
        lappend l_mid [ic_geo_get_point_location $mid]
        ic_delete_geometry point names $mid
        }
     }
   set pnt_l [concat $l_loc $l_mid]
   set x [lindex $xyz 0]
   set y [lindex $xyz 1]
   set z [lindex $xyz 2]
   foreach pnt $pnt_l {
     set n_p [scrib_lib_math_transform_cs $x $y $z $p1 $pnt]
     if {[llength $n_p] < 3} {
	mess "not getting three values from scrib_lib_math_transform_cs.  Getting $n_p.\n"
	set n_p [list 0 0 0]
     }
     lappend l_x [lindex $n_p 0]
     lappend l_y [lindex $n_p 1]
     lappend l_z [lindex $n_p 2]
     }
   set s_x [lsort -real  $l_x]
   set s_y [lsort -real  $l_y]
   set s_z [lsort -real  $l_z]
   set a [expr [lindex $s_x end]-[lindex $s_x 0]]
   set b [expr [lindex $s_y end]-[lindex $s_y 0]]
   set h [expr [lindex $s_z end]-[lindex $s_z 0]]
   return "$a $b $h"
}


proc scrib_lib_feature_find_container_math {p_c p_x p_y} {

  set v_x [scrib_lib_math_vector_sub   $p_x $p_c]
  set d_y [scrib_lib_math_vector_sub   $p_y $p_c]
  set v_z [scrib_lib_math_vector_vmult $v_x $d_y]
  set v_y [scrib_lib_math_vector_vmult $v_z $v_x]
  set n_x [scrib_lib_math_vector_nrm $v_x]
  set n_y [scrib_lib_math_vector_nrm $v_y]
  set n_z [scrib_lib_math_vector_nrm $v_z]
  return [list $n_x $n_y $n_z]
}


## set up to find the angle between uv values, where first is origin of angle.

proc scrib_lib_feature_find_angle_uv {uv1 uv2 uv3} {
    set angle 0
    set dist 0
    foreach i {0 1} {
	set p  [expr {[lindex $uv2 $i] - [lindex $uv3 $i]}]
	set dist [expr {$dist + $p * $p}]
    }
    set l23 [expr {sqrt($dist)}]
    set dist 0
    foreach i {0 1} {
	set p  [expr {[lindex $uv1 $i] - [lindex $uv2 $i]}]
	set dist [expr {$dist + $p * $p}]
    }
    set l12 [expr {sqrt($dist)}]
    set dist 0
    foreach i {0 1 } {
	set p  [expr {[lindex $uv1 $i] - [lindex $uv3 $i]}]
	set dist [expr {$dist + $p * $p}]
    }
    set l13 [expr {sqrt($dist)}]
    set temp [expr {acos(($l12*$l12+$l13*$l13-$l23*$l23)/(2*$l12*$l13))}]
    set angle [expr {$temp*180/3.1415926}]

    return $angle
}


proc scrib_lib_feature_find_opposite_curve {crv srf} {
 
  set oppst [scrib_lib_feature_find_opposite_side $srf]
  set plc   [lsearch $oppst $crv]
  if {$plc == -1} return
  if {$plc < 2} {set n_p [expr $plc+2]} else {set n_p [expr $plc-2]} 
  set crv_op [lindex $oppst $n_p]
  return $crv_op
}


## Needs doc

proc scrib_lib_feature_find_angle_between_curves {crv1 crv2 {pnt ""}} {
     return [ic_meshdirect_find_angle_between_curves $crv1 $crv2 $pnt]
}


## Needs doc

proc scrib_lib_feature_find_angle_between_surfs {{srf1 ""} {srf2 ""} {crv ""}} {
    
    if {$crv == ""} {
	if 0 {
	    foreach c [ic_geo_boundary surface $srf1 0 1] {
		if {[lsearch [ic_geo_boundary surface $srf2 0 1] $c] != -1} {set crv $c}
	    }
	} else {
	    # should be faster --jka
	    set crvs1 [lsort [ic_geo_boundary surface $srf1 0 1]]
	    set crvs2 [lsort [ic_geo_boundary surface $srf2 0 1]]
	    foreach c $crvs2 {
		if {0 <= [lsearch $crvs1 $c]} {
		    set crv $c
		    break
		}
	    }
	}
    }
    
    if {($srf1 == "") || ($srf2 == "")} {
	if {($srf1 == "") && ($srf2 == "")} {
	    set srfs [ic_geo_incident curve $crv 1]
	    set srf1 [lindex $srfs 0]
	    set srf2 [lindex $srfs 1]
	    if {$srf2 == ""} { return 0 }
	} else {
	    mess "can not use scrib_lib_feature_find_angle_between_surfs with only one defined surf.\n" red
	    return 0
	}
    }
    if {$crv == ""} {return ""}
    if {($srf1 == "") || ($srf2 == "")} { return 0 }
    set angle ""
    foreach pt [ic_geo_boundary curve $crv 0 1] {
	set uv_par1 [ic_geo_find_nearest_srf_pnt $srf1 $pt]
	set uv_par2 [ic_geo_find_nearest_srf_pnt $srf2 $pt]
	set tangent1 [lindex [ic_geo_get_srf_nrm [lindex $uv_par1 0] [lindex $uv_par1 1]  $srf1] 0]
	set tangent2 [lindex [ic_geo_get_srf_nrm [lindex $uv_par2 0] [lindex $uv_par2 1]  $srf2] 0]
	#mess "uv_par1 is $uv_par1, uv_par2 is $uv_par2\n" blue
	#mess "tangent1 is $tangent1 tangent2 is $tangent2\n" green
	lappend angle [expr [scrib_lib_math_angle_between_vectors $tangent1 $tangent2]  / 3.14 * 180]
    }
    #mess "angles are $angle\n"
    set a 0.0
    foreach ang $angle {set a [expr $a + $ang]}
    if [llength $angle] {set angle [expr $a / [llength $angle]]} else {set angle 0}
    return  $angle
}


##Needs doc

proc scrib_lib_feature_find_seam_buttons { {surfs ""} {exclusion_surfs ""}}  {
    return [ic_meshdirect_find_seam_buttons $surfs $exclusion_surfs]
}


## Needs doc  --> This procedure is not used anymore.

proc scrib_lib_feature_assign_buttons  {groups surface_subset curve_subset mesh_size {check_height 1}} {
    set ret_groups ""
    foreach nl $groups {
	set inner_outer [scrib_lib_geo_find_internal_outer_loops $nl]
	set outer_c [lindex $inner_outer 0]
	set inner_c [lindex $inner_outer 1]
	if {$inner_c == ""} {
	    #set all_crvs ""
	    #foreach s $nl {set all_crvs [concat  $all_crvs  [ic_geo_boundary surface $s 0 1]]}
	    #set inner_c [scrib_lib_geo_get_single_curves [lsort -unique $all_crvs]]
	    #if {[llength [scrib_lib_geo_make_conn_loops $inner_c]] > 1} {set inner_c ""}
	    foreach s $nl {set inner_c [concat $inner_c \
		[ic_geo_get_internal_surface_boundary $s]]}
	}
	if {$inner_c == ""} {
#	    if ![ic_geo_check_family $surface_fam]   {ic_geo_new_family $surface_fam}
#	    ic_geo_set_family surface $surface_fam names $nl 0	  
	    set ll ""
	    foreach s $nl {set ll [concat $ll surface $s] }
	    ic_geo_subset_add_items $surface_subset $ll
	    lappend ret_groups $nl
#	    if ![ic_geo_check_family $curve_fam]   {ic_geo_new_family $curve_fam}
#	    ic_geo_set_family curve $curve_fam names $outer_c 0	 
	    set ll ""
	    foreach c $outer_c {set ll [concat $ll curve $c] }
	    ic_geo_subset_add_items $curve_subset $ll
	    continue
	}
	if $check_height {
	    if {[lindex [scrib_lib_feature_find_container $inner_c] 2] > 1} {
		mess "container of internal curves is larger than 1, not making a button.\n"
		continue
	    }
	}
	set outer_length 0
	foreach c $outer_c {set outer_length [expr $outer_length + [ic_geo_crv_length $c]]}
	set inner_length 0
	foreach c $inner_c {set inner_length [expr $inner_length + [ic_geo_crv_length $c]]}
	foreach c $inner_c {
	    if {[ic_geo_check_objects_exist curve $c] == ""} {continue} 
	    ic_set_meshing_params curve $c emax [expr $inner_length / $outer_length  * $mesh_size] erat 0 ewid 0 ehgt 0
	}
#	if ![ic_geo_check_family $surface_fam]   {ic_geo_new_family $surface_fam}
#	ic_geo_set_family surface $surface_fam names $nl 0	 
	set ll ""
	foreach s $nl {set ll [concat $ll surface $s] }
	ic_geo_subset_add_items $surface_subset $ll
	lappend ret_groups $nl
#	if ![ic_geo_check_family $curve_fam]  {ic_geo_new_family $curve_fam}
	set ll ""
	foreach c $outer_c {
	    if {[ic_geo_check_objects_exist curve $c] == ""} {continue} 
#	    ic_geo_set_family curve $curve_fam names $c 0	
	    set ll [concat $ll curve $c] 
	}
	ic_geo_subset_add_items $curve_subset $ll
    }
    foreach {i j} [ic_geo_subset_get_items $curve_subset] {
	if {($i == "curve") && ([ic_geo_check_objects_exist curve $j] != "")} {
	    ic_set_meshing_params curve $j emax $mesh_size erat 1 ehgt $mesh_size ewid 1
	}
    }
    return $ret_groups
}

## Needs doc

proc scrib_lib_feature_find_single_bounded_buttons {surfs {exclusion_surfs ""}} {
    mess "scanning geometry for bounded buttons ...\n"
    set bounded_buttons ""

    if {$surfs == ""} {set surfs [ic_geo_get_objects surface ""]}
    if {$exclusion_surfs == ""} {
	foreach s [ic_geo_get_objects surface ""] {
	    if {[lsearch $surfs $s] == -1} {lappend exclusion_surfs $s}
	}
    }

   set part_boundary_single [scrib_lib_geo_find_part_boundary_single]
#	mess "part_boundary_single is $part_boundary_single\n"
    scrib_lib_exec_interrupt init
    set interrupt_pending 0
    set ntotal [llength $surfs]
    set ntt 0
    foreach surf $surfs {
	scrib_lib_exec_interrupt check $ntt $ntotal
	incr ntt
	if {$interrupt_pending == 1} {
 	    scrib_lib_exec_interrupt finish
	    return 0
 	}
	set singles [scrib_lib_geo_get_single_curves [ic_geo_boundary surface $surf 1 1]]
	set good_singles ""
	foreach s $singles {
	    if {[lsearch $part_boundary_single $s] == -1} {lappend good_singles $s}
	}
	if  {$good_singles == ""} continue
	if {[llength [scrib_lib_geo_make_conn_regions curve $good_singles]] < 2} continue
	set doubles [scrib_lib_geo_get_double_curves [ic_geo_boundary surface $surf 1 1]]	
	set conn_regions [scrib_lib_geo_make_conn_regions curve $doubles]
	if {[llength $doubles] < 2} continue

	set length_crvs [scrib_lib_geo_sort_by_crvs_length $conn_regions -decreasing ]
	set master_length [lindex [lindex $length_crvs 0] 0]
	set length_crvs [lreplace $length_crvs 0 0]
	foreach lc $length_crvs {
	    set crvs [lindex $lc 1]
	    set length [lindex $lc 0]
	    if {$length > [expr $master_length / 2]} continue
	    set new_surfs [scrib_lib_geo_find_internal_surfaces $crvs $surf]
	    if {$new_surfs != ""} {lappend bounded_buttons $new_surfs}
	}
    }
    scrib_lib_exec_interrupt finish
#    set bounded_buttons [scrib_lib_feature_assign_buttons $bounded_buttons $surface_fam $curve_fam $mesh_size]
    return $bounded_buttons
}

## Needs doc

proc scrib_lib_feature_find_button_features {{surfs ""}	{exclusion_surfs ""} {butt_type ""}} {
    return [ic_meshdirect_find_button_features $surfs $exclusion_surfs $butt_type]
}


proc scrib_lib_feature_validate_button_features {button_list {curves_subset ""} \
	{surf_subset "" } {mesh_size ""} {ratio 1} {width 1}} {

    set height [expr $mesh_size * $ratio]
    set i 1
    set button_surf_list ""
    foreach list $button_list {
	if {$list == ""} continue
	set inner_outer [scrib_lib_geo_find_internal_outer_loops $list 0 1]
	set outer [lindex $inner_outer 0]
	set inner [lindex $inner_outer 1]
#mess "outer is $outer, inner is $inner\n"
	if {$outer == ""} {
	    mess "no outer boundaries for $list\n" red
	    continue
	}

	#also find length of loop.  If internal curves are roughly the same size, we should not offset internal curves.
	set loop_length 0
	foreach c $outer {set loop_length [expr $loop_length + [ic_geo_crv_length $c]]}
	set not_good 0
	foreach c $inner {
	    if {[llength [ic_geo_incident curve $c 1]] > 1} {
		mess "getting non-single curves as inner_boundaries...\n" red
		set not_good 1
		break
	    }
	}
	if $not_good {continue}

	if {$inner != ""} {
	    if {[lindex [scrib_lib_feature_find_container "$inner"] 2] > 1} {
		mess "container of internal curves $inner\n is larger than 1, not making a button.\n"
		continue
	    }
	}
	lappend button_surf_list $list
	#put internal curves into a family if defined.
	if {$curves_subset != ""} {
	    set ll ""
	    foreach c [concat $outer $inner] {set ll [concat $ll curve $c ] }
	    ic_geo_subset_add_items $curves_subset $ll
#	    ic_geo_set_family curve $curves_fam names $outer	0
#	    ic_geo_set_family curve $curves_fam names $inner	0	
	}
	#assign a mesh size on all curves

	#All curves on boundary (i.e. loop) should get offset and set to mesh size
	foreach c $outer {
	    if {[ic_geo_check_objects_exist curve $c] == ""} {
		mess "curve $c does not exist\n"
		continue
	    } 
	    if {$mesh_size > 0} {ic_set_meshing_params curve $c emax $mesh_size erat 1 ehgt $mesh_size ewid 1}
	}
	#Find length of internal curves are roughly the same size, we should not offset internal curves.
	if [llength $inner] {
	    set int_loop_length 0
	    foreach c $inner {set int_loop_length [expr $int_loop_length + [ic_geo_crv_length $c]]}	    
	    if {$int_loop_length > [expr $loop_length * .5]} {set set_ratio 0} else {set set_ratio 1}
	    if {[llength [scrib_lib_geo_make_conn_loops $inner]] == 1} {set one_int_loop 1} else {set one_int_loop 0}
	    set ratioed_mesh_size $mesh_size
	    if {$int_loop_length && ($mesh_size > 0)} {set ratioed_mesh_size \
		[expr $int_loop_length / $loop_length  * $mesh_size]}
#mess "int_loop_length is $int_loop_length, loop_length is $loop_length, set_ratio is $set_ratio; \
#	one_int_loop is $one_int_loop\n" red
	}
	foreach c $inner {
	    if {[ic_geo_check_objects_exist curve $c] == ""} {
		mess "curve $c does not exist\n"
		continue
	    } 
	    #if it is a single curve, we should try to get the size from adjacent curves in $loop
	    if !$one_int_loop {
		 if {$mesh_size > 0} {ic_set_meshing_params curve $c emax $mesh_size erat 1  ewid 1 ehgt $mesh_size}
#mess "curve $c is getting $ratio\n" red
	    }  else {
		set crv $c
		set srf [ic_geo_incident curve $c 1]
#		set ms [lindex [ic_get_meshing_params curve $c] 1]
#		set number_of_nodes [expr  [ic_geo_crv_length $c] / $ms]
		set ii 0
		while 1 {
		    incr ii
#mess "crv is $crv; srf is $srf\n" grey
		    if {$crv != ""} {set crv [scrib_lib_feature_find_opposite_curve $crv $srf]}
#mess "crv is $crv; srf is $srf\n" blue
		    if {($crv == "") || ($ii > 10) } {
#mess "not finding matching pair for curve $c; crv is $crv\n"
			set csize [lindex [ic_get_meshing_params curve $c] 1] 
			if $set_ratio {
			    if {$mesh_size > 0} {ic_set_meshing_params curve $c emax $ratioed_mesh_size erat 1 ehgt $ratioed_mesh_size ewid 1}
			} else {
			    if {$mesh_size > 0} {ic_set_meshing_params curve $c emax $ratioed_mesh_size erat 0 ehgt 0 ewid 0}
			}
			break
		    }
		    set bounded 1
		    set number_of_nodes 0
		    set new_srf ""
		    foreach cc $crv {
			if {[lsearch $list $cc] == -1} {
			    set bounded 0
			}
			foreach s [ic_geo_incident curve $cc 1] {
			    if {$s != $srf} {lappend new_srf $s}
			}
		    }
		    if $bounded {
		 	foreach cc $crv {
			    set ms [lindex [ic_get_meshing_params curve $cc] 1]
			    set add_els [expr round([ic_geo_crv_length $cc] / $ms)]
			    if {$add_els == 0} {set add_els 1}
			    set number_of_nodes [expr  $number_of_nodes + $add_els ]
#mess "cc is $cc\n"
#mess "number of nodes for curve c makes it $number_of_nodes\n"
		        }
		        set new_size [expr [ic_geo_crv_length $c] / $number_of_nodes]
		        ic_set_meshing_params curve $c emax $new_size erat 0 ewid 0 ehgt 0
#mess "changing mesh params for curve $c.  Number of nodes are $number_of_nodes. crvs are $crv\n" blue
		        break 
		    }
#mess "new_srf is $new_srf\n"
		    set new_srf [lsort -unique $new_srf]

		    if {[llength $new_srf] == 1} {
			set srf $new_srf
		    } else {
			mess "curve $c is finding a multiple connection on offset\n"
			set crv ""
		    }
		}
	    }
	}
	#put button surfaces into a subset.	    
	set ll ""
	foreach s $list {set ll [concat $ll surface $s ] }
	if {$surf_subset == ""} {
	    set butt BUTTONS/BUTTON$i
	    #mess "$butt \n"
	    ic_geo_subset_add_items $butt $ll
	    incr i
	} else {
	    ic_geo_subset_add_items $surf_subset $ll
	    incr i
	}

    }
    set button_surf_list [lsort -unique $button_surf_list]
    return $button_surf_list
}

proc scrib_lib_feature_find_bounded_surface {group outer_c {exclusion_surfs ""}} {
#mess "group is $group/n outer_c is $outer_c\n" blue
    set sharp_corners [scrib_lib_feature_find_sharp_points $group 20 $outer_c]
    if {($sharp_corners == 0) || ([llength $sharp_corners] < 5)} {return ""}
#mess "sharp_corners are $sharp_corners\n"
    set next_surf_layer ""
    foreach c $outer_c {
	foreach s [ic_geo_incident curve $c 1] {
	    if {[lsearch $group $s] != -1} continue
	    if {[lsearch $exclusion_surfs $s] != -1} continue
	    lappend next_surf_layer $s
	}
    } 
    set add_surfs ""
    foreach surf $next_surf_layer {
	set tlength 0
	set blength 0
	foreach c [ic_geo_boundary surface $surf 1 1] {
	    set l [ic_geo_crv_length $c]
	    set tlength [expr $tlength + $l]
	    if {[lsearch $outer_c $c] != -1} {set blength [expr $blength + $l]}
	}
	if $tlength {
	    if {[expr $tlength / 2] < $blength} {
		lappend add_surfs $surf
		continue
	    }
	}
	set sharpies 0
	set sps ""
	foreach pt	[scrib_lib_feature_boundary_points $surf] {
	    if {[lsearch $sharp_corners $pt] !=  -1} {
		incr sharpies
		lappend sps $pt
	    }
	}

	if {$sharpies < 3} continue
	set sharp2 [scrib_lib_feature_find_sharp_points $surf 20]
	set OK 1
	foreach s $sps {
	    if {[lsearch $sharp2 $s] == -1} {
		set OK 0
		break
	    }
	}
	 if $OK {lappend add_surfs $surf}
    }
    return $add_surfs
}


## Find bounded surfaces attached to a connected region

proc scrib_lib_feature_find_bounded_surface_old {group outer_curves {num_open_curves 1} {exclusion_surfs ""}} {
  set i 0
  set add_surfs ""
  set att_surfs ""
  #mess "orig outer_curves are $outer_curves \n"
  while 1 {
    incr i
    if {$i > 10} {break}
    set new_added_surfs ""
    foreach crv $outer_curves {
	set surf ""
	foreach s [ic_geo_incident curve $crv 1] {
	    if {[lsearch $group $s] != -1} {continue}
	    if {[lsearch $exclusion_surfs $s] != -1} {continue}
	    lappend surf $s
	}
	set surf [lsort -unique $surf]
	foreach s $surf {
	    lappend crvs($s) $crv
	    lappend att_surfs $s
	}
    }
    set att_surfs [lsort -unique $att_surfs]
    set open_curves ""
    #set removal_outer_c ""
    foreach surf $att_surfs {
	set this_open_c ""
	foreach c [ic_geo_boundary surface $surf 1 1] {
	    if {[lsearch $crvs($surf) $c] != -1} continue
	    if {[llength [ic_geo_boundary curve $c 0 1]] == 1} continue
	    lappend open_curves $c
	    lappend this_open_c $c
	}
	if {[llength $this_open_c] > $num_open_curves} continue
	if {[lsearch $add_surfs $surf] != -1} continue
	lappend new_added_surfs $surf
	lappend group $surf
	#set removal_outer_c [concat $removal_outer_c $crvs($surf)]
    }
#mess "open_curves are $open_curves\n" blue
#     set outer_curves [scrib_lib_utils_list_remove_duplicated \
#	[concat $outer_curves $removal_outer_c [scrib_lib_utils_list_get_duplicate $open_curves]]]
    set outer_curves [lindex [scrib_lib_geo_find_internal_outer_loops $group] 0]

    if [llength $new_added_surfs] {
	set add_surfs [concat $add_surfs $new_added_surfs]
    } else {
	break
    }
#mess "new_added_surfs are $new_added_surfs i is $i, outer_curves are $outer_curves\n"
  }
   set add_surfs [lsort -unique $add_surfs]
  return [list $add_surfs $outer_curves]
}

## Find hanging surfaces.  Surfaces attached to a group that act as a cling-on

proc scrib_lib_feature_find_clingons_old {group {outer_c ""} {mesh_size 10}} {
    if {$outer_c == ""} {set outer_c [lindex [scrib_lib_geo_find_internal_outer_loops $group] 0]}
#mess "outer curves are $outer_c\n"
    #find all outer_pts
    set outer_pts ""
    foreach c $outer_c {
	set outer_pts [concat $outer_pts [ic_geo_boundary curve $c 0 1]]
    }
    set outer_pts [lsort -unique $outer_pts]
    #find all int_curves	
    set all_crvs ""
    foreach s $group {
	set all_crvs [concat $all_crvs [ic_geo_boundary surface $s 1 1]]
    }
    set all_crvs [lsort -unique $all_crvs]
    set int_curves [scrib_lib_utils_list_remove_duplicated [concat $all_crvs $outer_c]]
    set break_crvs ""
    set break_curves ""
#mess "internal curves are $int_curves\n"
    foreach c $int_curves {
	if {[ic_geo_crv_length $c] > $mesh_size} {continue}
	#at least one of the points connected to the curve must be on the outer boundary
	set b_pts 0
	foreach p [ic_geo_boundary curve $c 0 1] {
	    if {[lsearch $outer_pts $p] != -1} {
		incr b_pts
	    }
	}
	if {$b_pts > 1} {lappend break_curves $c}
	if {$b_pts == 1} {
	    lappend break_crvs $c
	    continue
	}
    }
#mess "break crvs are $break_crvs\n"
    #find connected break curves, and check angle
    #set break_curves ""
    foreach conn [scrib_lib_geo_make_conn_regions curve $break_crvs] {
#mess "conn is $conn\n" blue
	if {[llength $conn] < 2}  {continue}
	if {[llength $conn] == 2} {
	    if {[scrib_lib_feature_find_angle_between_curves [lindex $conn 0] \
		[lindex $conn 1]] < 20} {set break_curves [concat $break_curves $conn]}
	}
	if {[llength $conn] > 2} {
	    set conn_pts ""
	    foreach c $conn {
		foreach p [ic_geo_boundary curve $c 0 1] {
		    lappend att($p) $c
		}
		set conn_pts [concat $conn_pts [ic_geo_boundary curve $c 0 1]]
	   }
	    set dup_pts [scrib_lib_utils_list_get_duplicate $conn_pts]
	    foreach fp $dup_pts {
		if {[lsearch $outer_pts $fp] == -1} {set break_curves [concat $break_curves $att($fp)]}
	    }
	}
    }
#mess "break curves are $break_curves\n"
    set break_surfs ""
    #All bounding curves need to either be on outer boundary, a break curve, or smaller than the mesh size.
    foreach s $group {
	set break 1
	foreach c [ic_geo_boundary surface $s 0 1] {
	    if {([lsearch $outer_c $c] == -1) && ([lsearch $break_curves $c] == -1)} {
		set break 0
		break
	    }
	}
	if {[llength [lsort -unique [ic_geo_boundary surface $s 0 1]]] == 1} {set break 0}
	if $break {
	    lappend break_surfs $s
	    continue
	}
    }
#mess "clingon break surfs are $break_surfs\n" blue
    return $break_surfs
}


## Find surfaces that are isolated by all curves being on outer boundary, or forming a sharp angle
##with other surfaces in the group

proc scrib_lib_feature_find_sharpies {group outer_c angle {what surf}} {

    set my_sharpies ""
    
    # --> first make a hash with curve keys, each a list of surfs on it
    
    foreach s $group {
	foreach c [ic_geo_boundary surface $s 1 1] {
	    lappend boundary_curves_surfs($c) $s
	}
    }
    if ![info exists boundary_curves_surfs] { return "" }
    
    # --> remove any hash-entries which belong to the outer_c list
    
    foreach oc $outer_c {
	if [info exists boundary_curves_surfs($oc)] { unset boundary_curves_surfs($oc) }
    }
    
    # --> then look at the curves, any maybe append it's surfs to the sharpies list
    
    foreach c [array names boundary_curves_surfs] {
	if {[scrib_lib_feature_find_angle_between_surfs "" "" $c] < $angle } { continue	}
	concat my_sharpies $boundary_curves_surfs($c)
    }
    
    # --> uniquify, since a surface may have two curves which are looked at    
    
    return [lsort -unique $my_sharpies]
}


## Find sliver surfaces, or surfaces that have a high ratio of smallest curve length to largest curve length
## where the surface area is less than the smallest edge times largest edge times a ratio.

proc scrib_lib_feature_find_sliver_surfaces {surfs max_length {min_edge 5} {curvature 30} {ratio 1.1} } {
    set thin_surfs ""
    if {$surfs == ""} {set surfs [ic_geo_get_objects surface ""]}
    foreach surf $surfs {
#	mess "surf is $surf\n" blue
	if ![llength [ic_geo_check_objects_exist surface $surf]] {continue}
	if {[ic_surface_curvature $surf] > $curvature} continue
	set l ""
	foreach cl [ic_geo_boundary surface $surf 1 1] {
	    lappend l [list [ic_geo_crv_length $cl] $cl]
 	}
 	set l [lsort -index 0 -real -decreasing $l]
	if {[llength $l] < 2} continue
	set maxc [lindex [lindex $l 0] 0]
	set minc [lindex [lindex $l end] 0]
#	set meanc [lindex [lindex $l [expr int([llength $l] / 2) + 1] ] 0]

#	if {($maxc == "") || ($minc == "")}  continue
	if {[expr $maxc * $min_edge * $ratio] < [ic_geo_srf_area $surf] }  continue
	if { [expr $maxc / $minc]  < $max_length}  continue
	lappend thin_surfs $surf
#	    mess "surf $surf has ratio of $ratio\n" blue 	

    }
return $thin_surfs
}

## Combine thin surfaces into better meshable surfaces.
proc scrib_lib_feature_eval_combine_thin_surfaces {free_surfs {aspect_ratio 200} {min_edge 5} \
	{angle 30} {curvature 30}} {
  
    set thin_surfs [scrib_lib_feature_find_sliver_surfaces $free_surfs $aspect_ratio $min_edge $curvature]
mess "thin_surfs are $thin_surfs\n" debug
#    set loops [lsort -unique [scrib_lib_geo_make_conn_regions surface $thin_surfs]]
    set thin_loops_curvature ""
    set used_surfs ""

    mess "attempting to group thin surfaces ...\n"
    scrib_lib_exec_interrupt init
    set interrupt_pending 0
    set ntotal [llength $thin_surfs]
    while {$thin_surfs != ""} {
	set ntt [expr $ntotal - [llength $thin_surfs]]
	scrib_lib_exec_interrupt check $ntt $ntotal
	if {$interrupt_pending == 1} {
 	    scrib_lib_exec_interrupt finish
	    return 0
 	}
	set surf  [lindex $thin_surfs 0]
	set current_surfs $surf
	set tot_curvature [ic_surface_curvature $surf]
	set thin_surfs [lreplace $thin_surfs 0 0]
	foreach curve [ic_geo_boundary surface $surf 1 1] {
	    if {[ic_geo_crv_length $curve] < [expr $aspect_ratio / $min_edge]} continue
	    if {[scrib_lib_feature_find_angle_between_surfs "" "" $curve] > $angle } continue
	    foreach s [ic_geo_incident curve $curve 1] {
		if {[lsearch $current_surfs $s] != -1} continue
		if {[lsearch $free_surfs $s] == -1} continue
		if {[ic_surface_curvature $s] > $curvature} continue
		set new_curvature [expr $tot_curvature + [ic_surface_curvature $s]]
		if {$new_curvature > 120} continue
		if {[lsearch $used_surfs $s] != -1} {
		#if we find that one of the neighbor surfaces is in another group, we need to combine the groups
		    set new_thin_loops ""
		    foreach l $thin_loops_curvature {
			set ll [lindex $l 0]
			if {[lsearch $ll $s] == -1} {
			    lappend new_thin_loops $l
			} else {
			    set new_curvature [expr [lindex $l 1] + $new_curvature]
#mess "new curvature for $ll and $current_surfs is $new_curvature\n"
			    if {$new_curvature > 120} {
				lappend new_thin_loops $l
			    } else {
				set current_surfs [concat $current_surfs $ll]
				set tot_curvature $new_curvature
			    }
			}
		    }
		    set thin_loops_curvature $new_thin_loops
		} else {
		    lappend current_surfs $s
		    set tot_curvature $new_curvature
		    set place [lsearch $thin_surfs $s]
		    if {$place != -1} {	set thin_surfs [lreplace $thin_surfs $place $place]}
		}
	    }
	}
	lappend thin_loops_curvature [list $current_surfs $tot_curvature]
	set used_surfs [concat $used_surfs $current_surfs]
    }
    scrib_lib_exec_interrupt finish
    set thin_loops ""
    foreach l $thin_loops_curvature {
	lappend thin_loops [lindex $l 0]
    }

return $thin_loops
}


## Method to find smart loops

proc scrib_lib_feature_find_smart_loop_surfs {surfs mesh_size min_edge boxheight \
	min_surf_area {factor 1} {angle 70} {max_curvature 180} {exclusion_surfs ""} } {
#find normal with ic_geo_get_srf_nrm 0.5 0.5 surf
#find curvature with ic_surface_curvature surf
    if ![llength $surfs] {
	mess "scrib_lib_feature_find_smart_loop_surfs called with no surfs\n"
	return
    }
    set trouble_surfs ""
#Find Trouble Surfaces

    mess "scanning geometry for problem surfaces ...\n"
    scrib_lib_exec_interrupt init
    set interrupt_pending 0
    set ntotal [llength $surfs]
    set ntt 0
    foreach surf $surfs {
	scrib_lib_exec_interrupt check $ntt $ntotal
	incr ntt
	if {$interrupt_pending == 1} {
 	    scrib_lib_exec_interrupt finish
	    return 0
 	}
	if ![llength [ic_geo_check_objects_exist surface $surf]] {continue}
	set curvature($surf) [ic_surface_curvature $surf]
	set normal($surf) [ic_geo_get_srf_nrm 0.5 0.5 $surf]
	set area($surf) [ic_geo_srf_area $surf]
	#set smallest_edge($surf)
	set length 1000000
	foreach c [ic_geo_boundary surface $surf 1 1] {
		set nlength [ic_geo_crv_length $c]
		if {$nlength < $length} {set length $nlength}
	}
	set smallest_edge($surf) $length

	#if {$curvature($surf) > 90} continue
	if {$area($surf) > $min_surf_area} continue
	if {$smallest_edge($surf) > $min_edge} continue
	if {$curvature($surf) > $max_curvature} continue
	lappend trouble_surfs $surf
    }
    scrib_lib_exec_interrupt finish

    if ![llength $trouble_surfs] {return ""}
#mess "trouble_surfs are $trouble_surfs\n"
    set groups [scrib_lib_geo_make_conn_regions surface $trouble_surfs]
    set groups [lsort -unique $groups]
    set ret_groups [scrib_lib_feature_handle_smart_regions $groups $mesh_size \
	$min_edge $boxheight $min_surf_area $factor $angle $max_curvature $exclusion_surfs]
    return $ret_groups
}

proc scrib_lib_feature_handle_smart_regions { groups mesh_size min_edge boxheight \
	min_surf_area {factor 1} {angle 70} {max_curvature 180} {exclusion_surfs ""} } {
    set ret_groups ""
set timer [clock seconds]
    mess "Attempting to group problem surfaces ...\n"
    scrib_lib_exec_interrupt init
    set interrupt_pending 0
    set ntotal [llength $groups]
    set ntt 0
    foreach group $groups {
	scrib_lib_exec_interrupt check $ntt $ntotal
	incr ntt
	if {$interrupt_pending == 1} {
 	    scrib_lib_exec_interrupt finish
	    return 0
 	}
	set group [lsort -unique $group]
#mess "group is $group\n\n"
	set inner_outer [scrib_lib_geo_find_internal_outer_loops $group 1]
	set outer_c [lindex $inner_outer 0]
	set inner_c [lindex $inner_outer 1]
#mess "inner_c is $inner_c\n"
	set i 0
	while 1 {
	    if {$i > 5} {
		mess "Caught in loop trying to add internal surfaces and bounded surfaces!\n" red
		break
	    }
	    #try to add internal surfaces to group
	    set j 0
	    while {$inner_c != ""} {
		incr j
		if {$j > 5} {
		    mess "Caught in loop trying to add internal surfaces!\n" red
		    mess "Loop is $group\n"
		    break
		}
		set add_surfs [scrib_lib_geo_find_internal_surfaces $inner_c $group $outer_c $exclusion_surfs]
	    	set group [concat $group $add_surfs]
		set group [lsort -unique $group]
		set inner_outer [scrib_lib_geo_find_internal_outer_loops $group 1]
	    	set outer_c [lindex $inner_outer 0]
	    	set inner_c [lindex $inner_outer 1]
#		set inner_c [scrib_lib_geo_get_double_curves $inner_c]
		if {$inner_c == ""} break
		#should not be any inner_curves after this!
	    }
	    #Find any bounded surfaces
	    set add_surfs2 [scrib_lib_feature_find_bounded_surface $group $outer_c $exclusion_surfs]
	    set add_surfs2 [lsort -unique $add_surfs2]
	    if {$add_surfs2 != "" } {
		set group [concat $group $add_surfs2]
		set group [lsort -unique $group]
		set inner_outer [scrib_lib_geo_find_internal_outer_loops $group 1]
		set outer_c [lindex $inner_outer 0]
		set inner_c [lindex $inner_outer 1]
		if {$inner_c == ""} break
	   } else { break}
	}
	set group [lsort -unique $group]
	set inner_outer [scrib_lib_geo_find_internal_outer_loops $group 1]
	set outer_c [lindex $inner_outer 0]
#mess "group is $group\n outer_c is $outer_c\n"
	set sharp_corners [scrib_lib_feature_find_sharp_points $group 60 $outer_c]
#mess "sharp_corners are $sharp_corners\n"
	set used_pts ""
	set small_edge_set ""
	foreach sc $sharp_corners {
	    if {[lsearch $used_pts $sc] != -1} continue
	    foreach c [ic_geo_incident point $sc 1] {
		if {[lsearch $outer_c $c] == -1} {continue}
		lappend used_pts $sc
		set curve $c
		set length [ic_geo_crv_length $curve]
		set curves $c
		while {$length < $min_edge} { 
		    set breakto 0
#mess "length is $length\n"
		    set point ""
		    foreach p [ic_geo_boundary curve $curve 0 1] {
			if {[lsearch $used_pts $p] != -1} {
			    set breakto 1
			    continue
			}
			if {[lsearch $sharp_corners $p] != -1} {
#mess "small edge in group $group found to be $curves\n"
			    lappend small_edge_set [list $curves [list $p $sc] ]
			    set breakto 1
			    break
			}
		    	set point $p
			lappend used_pts $p
		    }
		    if $breakto {break}
		    #if {$point == ""} {continue}
		    set new_curve ""
		    foreach cc [ic_geo_incident point $point 1] {
			if {[lsearch $outer_c $cc] == -1} {continue}
			if {[lsearch $curves $cc] == -1} {
			    set new_curve $cc
			    break
			}
		    }
		    if {$new_curve == ""} {
			mess "not getting next curve\n" red
			break
		    } else { set curve $new_curve}
		    set length [expr $length + [ic_geo_crv_length $curve]]
		    lappend curves $curve
		} 
	    }
	}
	if [llength $small_edge_set] {
mess "small_edge_set is $small_edge_set\n"
	    foreach eee $small_edge_set {
		set edges [lindex $eee 0]
		set vertices [lindex $eee 1]
		
		
	    }
	}
	lappend ret_groups $group
    }
    scrib_lib_exec_interrupt finish
    set ret_groups [scrib_lib_feature_check_angle_smart_regions  $ret_groups $angle ]
    return $ret_groups
}


proc scrib_lib_feature_check_angle_smart_regions { groups {angle 70} {exclusion_surfs ""} } {
 
    set ret_groups ""
    set timer2 [clock seconds]
    mess "scanning groups for sharp angles ...\n"
    scrib_lib_exec_interrupt init
    set interrupt_pending 0
    set ntotal [llength $groups]
    set ntt 0
    foreach group $groups {
	scrib_lib_exec_interrupt check $ntt $ntotal
	incr ntt
	if {$interrupt_pending == 1} {
 	    scrib_lib_exec_interrupt finish
	    return 0
 	}
	if {[llength $group] == 1} {continue}
	set inner_outer [scrib_lib_geo_find_internal_outer_loops $group]
	set outer_c [lindex $inner_outer 0]
	set sharp_surfs  [lsort -unique [scrib_lib_feature_find_sharpies $group $outer_c $angle curve]]
	if [llength $sharp_surfs] {
#	    mess "group is $group\n"
	    mess "sharpies are $sharp_surfs\n"
	    if {[llength $sharp_surfs] == [llength $group] } continue
	    foreach s $sharp_surfs {
		set place [lsearch $group $s]
		set group [lreplace $group $place $place]
	    }
	    set new_groups [scrib_lib_geo_make_conn_regions surface $group]
	    set new_groups [lsort -unique $new_groups]
	    foreach n $new_groups {
		if {[llength $n] > 1} {lappend ret_groups $n}
	    }
	} else {lappend ret_groups $group}
    }
    scrib_lib_exec_interrupt finish
mess "took [expr [clock seconds] - $timer2] seconds to check/fix angles\n" blue
#mess "ret_groups are $ret_groups\n"
    return $ret_groups
}


## Evaluate surface inner and outer boundaries to see if there are any potential meshing problems


proc scrib_lib_feature_find_grouped_inner_outer_regions {inner_loops free_surfs} {
#    if {$free_surfs == ""} {set free_surfs [ic_geo_get_objects surface ""]}
    set inner_loops_list [scrib_lib_geo_sort_by_crvs_length $inner_loops -decreasing]
    set inner_loops ""
    foreach i $inner_loops_list {lappend inner_loops [lindex $i 1]}
    #set ret_inner_outer_surfs ""
#mess "inner_loops are $inner_loops\n" 
    set all_surround_surfs ""
    while {$inner_loops != ""} { 
	set current_inner_loops [list [lindex $inner_loops 0]]
#mess "current_inner_loops is $current_inner_loops\n" green
	set inner_loops [lreplace $inner_loops 0 0]
	#The inner_loops are going to be attached to surfaces that are in free_surfs.
	#We want to group the surrounding surfaces, and find the outer curves of these surrounding surfaces.  
	#This group of surfaces should then evaluate the inner connected regions with other inner connected regions as well as the boundaries.
	#At the boundaries, we then need to either add neighbor surface, or split the boundary.
	set current_surround ""
	set surround_surfs ""
	set grouped_inner_loops $current_inner_loops
	while 1 {
#mess "current_inner_loops are $current_inner_loops\n"
	    foreach current_inner_loop $current_inner_loops {
#mess "current_inner_loop is $current_inner_loop\n" blue
#mess "current_inner_loops are $current_inner_loops\n" blue
		foreach c $current_inner_loop {
#mess "c is $c\n" blue
		    foreach s [ic_geo_incident curve $c 1] {
		        if {[lsearch $free_surfs $s] == -1} continue
		        if {[lsearch $all_surround_surfs $s] != -1} continue
		        if {[lsearch $current_surround $s] != -1} continue
		        lappend current_surround $s
		    }
#mess "current_surround is $current_surround\n" blue
		}
	    }
	    if {$current_surround == ""} {
#mess "breaking here ----------------\n"
		break
	    }
	    set surround_surfs [concat $surround_surfs $current_surround]
	    set all_surround_surfs [concat $all_surround_surfs $current_surround]
#mess "surround_surfs are $surround_surfs\n" blue
	    set current_surround ""
	    set boundaries ""
	    foreach s $surround_surfs {
		set boundaries [concat $boundaries [ic_geo_boundary surface $s 0 1]]
	    }
	    foreach gr $grouped_inner_loops {
		foreach l $gr {
		    set boundaries [concat $boundaries $l]
		}
	    }
#mess "boundaries are $boundaries\n"
	    set outer_c [scrib_lib_utils_list_remove_duplicated $boundaries]
#mess "outer_c is $outer_c\n" blue

	    set current_inner_loops ""
	    foreach il $inner_loops {
#mess "inner_loops are $inner_loops\n" blue
		foreach o $outer_c {
		    if {[lsearch $il $o] != -1} {
			if {[lsearch $current_inner_loops $il] != -1} continue
#mess "adding $il to $current_inner_loops\n"
			lappend current_inner_loops $il
			lappend grouped_inner_loops $il
		    }
		}
	    }
	    if {$current_inner_loops == ""} break
	    foreach cil $current_inner_loops {
		set place [lsearch $inner_loops $cil]
		set inner_loops [lreplace $inner_loops $place $place]
	    }
#mess "inner_loops are $inner_loops\n" green
	}
	if {$surround_surfs == ""} {continue}
	if ![info exists ret_inner_outer_surfs] {
	     set ret_inner_outer_surfs [list [list $grouped_inner_loops $outer_c $surround_surfs]]
	} else {
	     lappend ret_inner_outer_surfs [list $grouped_inner_loops $outer_c $surround_surfs]
	}
#mess "ret_inner_outer_surfs are $ret_inner_outer_surfs\n"
    }
    if ![info exists ret_inner_outer_surfs] {set ret_inner_outer_surfs ""}    
    return $ret_inner_outer_surfs
}

proc scrib_lib_feature_eval_fix_collisions {inner_loops free_surfs tolerance min_split_tol {curvature 60}} {

    set in_out_gsurfs [scrib_lib_feature_find_grouped_inner_outer_regions $inner_loops $free_surfs]
    set all_grouped_surfs ""
    set new_groups ""
    foreach iog $in_out_gsurfs {
	set inner_curves [lindex $iog 0]
	set outer_c [lindex $iog 1]
	set grouped_surfs [lindex $iog 2]
	set aaa ""
	foreach s $grouped_surfs {
	    if {[lsearch $all_grouped_surfs $s] != -1} continue	 
	    lappend aaa $s       
	}
	set grouped_surfs $aaa
	set all_grouped_surfs [concat $all_grouped_surfs $grouped_surfs]
#mess "all_grouped_surfs are $all_grouped_surfs\n" red
#mess "inner_curves are $inner_curves\n"
	set surfs_to_add ""
	foreach inner_c $inner_curves {
#mess "inner_c is $inner_c\n"
	    foreach i $inner_c {
		foreach o $outer_c {
#mess "o is $o, i is $i\n"
		    set pt [ic_geo_create_points_curveinter [list $i $o] $tolerance INT]
		    if {$pt == ""} {continue}
		    foreach surf [ic_geo_incident curve $o 1] {
#			if {[lsearch $grouped_surfs $surf] != -1} continue
		   	if {[lsearch $free_surfs $surf] == -1} continue
			if {[lsearch $all_grouped_surfs $surf] != -1} continue
			if {[lsearch $surfs_to_add $surf]  != -1} continue
#mess "angle is [scrib_lib_feature_find_angle_between_surfs "" "" $o]\n" blue
			if {[scrib_lib_feature_find_angle_between_surfs "" "" $o] > 60} continue
			if {[ic_surface_curvature $surf] > $curvature} continue
			lappend surfs_to_add  $surf 
			lappend all_grouped_surfs $surf
		    }
		    ic_delete_geometry point names $pt
		}
#mess "surfs_to_add are $surfs_to_add\n" green
	    }
#mess "surfs_to_add are $surfs_to_add\n"
	}
	if {$surfs_to_add == ""} continue
#	set surfs_to_add [lsort -unique $surfs_to_add]
#	mess "surfaces $surfs_to_add have curves in proximity of $tolerance\n"		 
	if {$new_groups == ""} {
	    set new_groups [list [concat $grouped_surfs $surfs_to_add]]
	} else {
   	    lappend new_groups  [concat $grouped_surfs $surfs_to_add]
	}
#		    foreach c {i o} {
#			set ploc($c) [ic_geo_project_point curve $c]
#			set too_close 0
#			foreach bpt [ic_geo_boundary curve $c 0 1] {
#			    set loc [ic_geo_get_point_location $bpt]
#			    set dist [ic_geo_pnt_dist $loc $ploc($c)]
#			    if {$dist < $min_split_tol} {
#				mess "intersection is too close\n"
#				set too_close 1
#			    } 
#			}
#		    }
    }
     if ![info exists new_groups] {set new_groups ""}   
     return $new_groups
}


proc scrib_lib_feature_find_sharp_points {group {max_angle 30} {outer_c ""}} {
    if {$outer_c == ""} { 
	set out_in [scrib_lib_geo_find_internal_outer_loops $group]
	set outer [lindex $out_in 0]
    } else {set outer $outer_c}
    set outer_pts ""
    foreach c $outer {set outer_pts [concat $outer_pts [ic_geo_boundary curve $c 0 1]]}
    set outer_pts [lsort -unique $outer_pts]
    set bad_corners ""
    foreach p $outer_pts {
	set crvs [ic_geo_incident point $p 1]
	set cc ""
	foreach c $crvs {
		if {[lsearch $outer $c] == -1} continue
		lappend cc $c
	}
	if {[llength $cc] != 2} {
	    lappend bad_corners $p
#mess "Finding strange connection at curves $cc...\n Connections at $p are $cc\n Skipping\n" red
	    continue
	    #set cc [lindex $cc 0]
	}

	set angle [scrib_lib_feature_find_angle_between_curves \
		[lindex $cc 0]  [lindex $cc 1] $p] 
	set ang2 [expr 180 - $max_angle]
	if {($angle < $max_angle) || ($angle > $ang2) } {continue}
	#mess "angle is $angle\n"
	lappend bad_corners $p
    }
    return $bad_corners
}


proc scrib_lib_feature_boundary_points {surf} {
    set outer [ic_geo_boundary surface $surf 0 1]
    set outer_pts ""
    foreach c $outer {set outer_pts [concat $outer_pts [ic_geo_boundary curve $c 0 1]]}
    return [lsort -unique $outer_pts]
}


## This procedure used to be in mesh_quad.tcl for batch meshing.  Now making it a seperate procedure to group surfaces based on feature info
## features is a list of what to run, list can contain bolt, flange, fillet, button smart_loop
## bolt_args is list of arg value pairs, where arg can be ratio min_hole max_hole hole_4 hole_6 hole_8 hole_10 hole_12
## flange_args is list of arg value pairs, and args must contain: flange_width flange_curvature flange_width_min notch_length ignore_tol 
## fillet_args is list of arg value pairs, and args must contain: length_min length_2 length_3 length_4 length_5 ang_min ang_max
## button_args is list of arg value pairs, and args must contain: bead_mesh_size Buttons_free Buttons_split Buttons_fillet
## smart_loop_args is list of arg value pairs, and args must contain: 
## ring_conflict conflict_distance sl_max_curvature sl_angle sl_min_surf_area sl_min_edge mesh_size thin_surfs sl_aspect_ratio

proc scrib_lib_feature_detection { features ratio number_rings {bolt_args ""} {flange_args ""} \
	{fillet_args ""} {button_args ""} {smart_loop_args ""}} {
    #
    #==================== FEATURE DETECTION ==============
    #
    set flange_loops ""
    set butt_loops ""
    set butt_surfs ""
    set free_butt ""
    set fillet_loops ""
    set split_butt_loops ""
    set free_surfs [ic_geo_get_objects surface ""]
    set smart_loops ""
    set exclusion_surfs ""
    set thin_loops ""
    set wider_groups ""
    set ratio [expr 1 / $ratio ]
    set width $number_rings
    #
    #==================== BOLT HOLES ==============
    #
    # --> set mesh sizes, extra on bolt holes!
    #
    if {[lsearch $features bolt] != -1} {
	foreach {n v} $bolt_args {set $n $v}
	lappend bolt_args ratio $ratio
	foreach possible_arg [list min_hole max_hole hole_4 hole_6 hole_8 hole_10 hole_12] {
	    if ![info exists $possible_arg] {set $possible_arg 0}
	    if {$possible_arg < 1} {set $possible_arg 0}
	}
	# This line removes holes less than min_hole
	scrib_lib_meshdirect_bolt_holes 0 $min_hole 0
	#This line puts 4 nodes on hole between min_hole and med_hole.
	scrib_lib_meshdirect_bolt_holes $min_hole $hole_4 4 $ratio $width
	#This line puts 6 nodes on hole between med_hole and max_hole.
	scrib_lib_meshdirect_bolt_holes $hole_4 $hole_6 6 $ratio $width
	scrib_lib_meshdirect_bolt_holes $hole_6 $hole_8 8 $ratio $width
	scrib_lib_meshdirect_bolt_holes $hole_8 $hole_10 10 $ratio $width
	scrib_lib_meshdirect_bolt_holes $hole_10 $hole_12 12 $ratio $width
	scrib_lib_meshdirect_bolt_holes $hole_12 $max_hole "" $ratio $width
    }
    #
    #================= FLANGES =====================
    #
    #
    #--> find flange surfaces.
    #
    if {[lsearch $features flange] != -1} {
	foreach {n v} $flange_args {set $n $v}
	foreach possible_arg [list flange_width flange_curvature flange_width_min notch_length ignore_tol ] {
	    if ![info exists $possible_arg] {
		mess "scrib_lib_feature_detection has been called for flanges, but arg $possible_arg is not in flange_args list.\n Can not continue\n" red
		return
	    }
	}
	mess "Finding flange regions...\n\n"
	set flanges [scrib_lib_flanges_find $flange_width $flange_curvature $flange_width_min "" $notch_length $ignore_tol]
	#flanges is a list of [list $region $ifp $sidef $ofp $sideo $olp $sidel $ilp $sidec]
	#				sidec
	# 	 ifp x-----------------------------------x ilp
	# sidef     |                                            |      sidel
	# 	ofp x-----------------------------------x olp
	#				sideo
if 0 {
	set i 0
	foreach flange $flanges {
	    incr i
	    set corners [list [lindex $flange 1] [lindex $flange 3] [lindex $flange 5] [lindex $flange 7] ]
	    set flange_surfs [lindex $flange 0] 
	    set free_surfs [scrib_lib_utils_remove_from_grouped $flange_surfs $free_surfs]
	    set f_loop [scrib_lib_quad_create_loop_for_group $thisscript_fam $flange_surfs {} $corners]
	    ic_set_meshing_params loop $f_loop emax 0.0 ehgt 0.0 emin 0.0 edev 0.0 erat 0 ewid 0 etyp 5

	    if ![ic_geo_check_family FLANGES/GROUP$i]   \
		{ic_geo_new_family FLANGES/GROUP$i}
	    ic_geo_set_family surface FLANGES/GROUP$i names [lindex $flange 0] 0
	}
}
if 1 {
	set i 0
	foreach flange $flanges {
	    incr i
	    set surfaces [lindex $flange 0]
	    set sidef [lindex $flange 2]
	    set sideo [lindex $flange 4]
	    set sidel [lindex $flange 6]
	    set sidec [lindex $flange 8]
#mess "sidef is $sidef; sidel is $sidel; sideo is $sideo; sidec is $sidec\n" blue
	    set OK 1	    
	    foreach c [list $sidef $sidel] {
		set c [scrib_lib_flanges_get_valid_curves $c $ignore_tol]
		#mess "c is $c\n" debug
		if {[llength $c] > 2} {
		    set OK 0
		    mess "Warning: can't set flange thickness on side with more than 2 curves, for surf $flanges, $c\n" red
		    break
		}
		if {[llength $c] == 2} {
		    foreach cc $c {ic_set_meshing_params curve $cc emax 1000000}
		    continue
		}
		if {[llength $c] == 1} {
		    ic_set_meshing_params curve $c emax [expr [ic_geo_crv_length $c] / 2]
		}
	    }
	    if !$OK {continue}
	    scrib_lib_flanges_assign_free_curves $surfaces $sideo $sidec

	    if ![ic_geo_check_family FLANGES/GROUP$i]   \
		{ic_geo_new_family FLANGES/GROUP$i}
	    ic_geo_set_family surface FLANGES/GROUP$i names [lindex $flange 0] 0
	    
	    lappend flange_loops [lindex $flange 0] 
	 }
	set free_surfs [scrib_lib_utils_remove_from_grouped $flange_loops $free_surfs]
}
    }

    #
    #================= FILLETS =====================
    #
    if {[lsearch $features fillet] != -1} {
	foreach {n v} $fillet_args {set $n $v}
	foreach possible_arg [list length_min length_2 length_3 length_4 length_5 ang_min ang_max] {
	    if ![info exists $possible_arg] {
		mess "scrib_lib_feature_detection has been called for fillets, but arg $possible_arg is not in fillet_args list.\n Can not continue\n" red
		return
	    }
	}
 
	mess "Finding fillet regions...\n\n"
	set fillet_sizes [list \
		  [list [list $length_min $length_2] 2] \
		  [list [list $length_2 $length_3] 3] \
		  [list [list $length_3 $length_4] 4] \
		  [list [list $length_4 $length_5] 5] \
	    ]
	#mess "fillet sizes are $fillet_sizes\n"
	set fillet_regions [scrib_lib_fillet_find_surf $ang_min $ang_max $length_min 3 "" 1 $fillet_sizes]
	set open_fillets [lindex $fillet_regions 0]
	set closed_fillets [lindex $fillet_regions 1]
	mess "fillet regions are $fillet_regions\n"
	if {$open_fillets != ""} {
	    if ![ic_geo_check_family FILLET]   {ic_geo_new_family FILLET}
	    ic_geo_set_family surface FILLET names $open_fillets 0
	}
    } 

    #
    #================= BUTTONS =====================
    #
    #
    #--> find button surfaces.
    #
    if {[lsearch $features button] != -1} {
	foreach {n v} $button_args {set $n $v}
	foreach possible_arg [list bead_mesh_size Buttons_free Buttons_split Buttons_fillet] {
	    if ![info exists $possible_arg] {
		mess "scrib_lib_feature_detection has been called for buttons, but arg $possible_arg is not in button_args list.\n Can not continue\n" red
		return
	    }
	}   
	mess "Finding button surfaces...\n\n"
    
	set butt_loops [scrib_lib_feature_find_button_features $free_surfs ]

	set butt_loops [scrib_lib_feature_validate_button_features $butt_loops \
	    BUTTONS/BUTTON_CURVES  BUTTONS/BUTTONS $bead_mesh_size $ratio $width]

	set free_surfs [scrib_lib_utils_remove_from_grouped $butt_loops $free_surfs]
    
	set l [llength $butt_loops]
	if $l { mess "$l buttons found\n\n" }

	#
	#--> find free edged buttons
	#
	if {$Buttons_free == 1} {
       
	    mess "Finding free edged buttons...\n\n"
    
	    set free_butt [scrib_lib_feature_find_single_bounded_buttons $free_surfs]
    
	    set free_butt [scrib_lib_feature_validate_button_features $free_butt \
		BUTTONS/FREE_BUTTON_CURVES  BUTTONS/FREE_BUTTONS $bead_mesh_size $ratio $width]

	    set free_surfs [scrib_lib_utils_remove_from_grouped $free_butt $free_surfs]

	    set l [llength $free_butt]
	    if $l { mess "$l buttons found\n\n" }
	}
	#
	#-->find split buttons stuff
	#
	if {$Buttons_split == 1} {
        
	    mess "Finding split buttons...\n\n"
    
	    set split_butt_loops [scrib_lib_feature_find_seam_buttons $free_surfs] 

	    set split_butt_loops [scrib_lib_feature_validate_button_features $split_butt_loops \
		BUTTONS/SPLIT_BUTTON_CURVES  BUTTONS/SPLIT_BUTTONS $bead_mesh_size $ratio $width]

	    set free_surfs [scrib_lib_utils_remove_from_grouped $split_butt_loops $free_surfs]

	    set l [llength $split_butt_loops]
	    if $l {mess "$l buttons found\n\n"}
	}
	#
	#-->find fillet buttons stuff
	#
	if {$Buttons_fillet == 1} {
	    mess "Finding fillet buttons...\n\n"    

	    set fillet_loops [scrib_lib_fillet_find_by_arc $free_surfs 1 20 15 5000 2000 2]

	    set fillet_loops [scrib_lib_feature_validate_button_features $fillet_loops \
		BUTTONS/FILLET_BUTTON_CURVES  BUTTONS/FILLET_BUTTONS $bead_mesh_size $ratio $width]
 
	    set free_surfs [scrib_lib_utils_remove_from_grouped $fillet_loops $free_surfs]

	    set l [llength $fillet_loops]
	    if $l {mess "$l buttons found\n\n"}
	}
	#
	#--> Look for duplicate surfaces in the buttons and combine into butt_loops
	#
	set butt_loops [concat $butt_loops $free_butt $fillet_loops $split_butt_loops]

	set butt_loops [scrib_lib_utils_list_olists_combine_dup_items $butt_loops]

	if {$butt_loops != ""} {
	    set l [llength $butt_loops]
	    if $l {mess "Total of $l buttons found\n\n" blue}

	}
    }

    #
    #============= SMART LOOP BOUNDARIES AROUND HOLES AND BUTTONS==========
    #
    # get all curves in HOLE to later try to add layers of surfaces or segment for better meshing.
    if {[lsearch $features smart_loop] != -1} {
	foreach {n v} $smart_loop_args {set $n $v}
	foreach possible_arg [list ring_conflict conflict_distance sl_max_curvature sl_angle sl_min_surf_area sl_min_edge \
	    mesh_size thin_surfs sl_aspect_ratio] {
	    if ![info exists $possible_arg] {
		mess "scrib_lib_feature_detection has been called for smart_loops, but arg $possible_arg is not in smart_loop_args list.\n Can not continue\n" red
		return
	    }
	}   
	mess "finding boundaries at conflict distance\n"

	set inner_groups  [scrib_lib_geo_make_conn_regions curve [ic_geo_get_objects curve HOLE]]

	foreach blp $butt_loops {			  
	lappend inner_groups [lindex [scrib_lib_geo_find_internal_outer_loops $blp] 0]
	}

	mess "Orienting surfaces consistently...\n"
	ic_geo_surface_normals_orient [lindex $free_surfs 0]

	if {($ring_conflict == 1) && ($inner_groups != "")}  {
	    if {$conflict_distance != 0} {
		set grouped_surfs ""
		set wider_groups [scrib_lib_feature_eval_fix_collisions $inner_groups $free_surfs \
		    $conflict_distance 5 $sl_max_curvature]

		set free_surfs [scrib_lib_utils_remove_from_grouped $wider_groups $free_surfs]

		set i 0
		foreach loop $wider_groups {
		    incr i
		    set ll ""
		    foreach s $loop {set ll [concat $ll surface $s] }
		    ic_geo_subset_add_items CONFLICT_BOUND/GROUP$i $ll
		}
	    }
	}
	#
	#================= SMART LOOPS =================
	#
	mess "Finding smart loops...\n\n"
	 foreach surf [ic_geo_get_objects surface ""] {
	    if {[lsearch $free_surfs $surf] == -1} {lappend exclusion_surfs $surf}
	}
	set smart_loops [scrib_lib_feature_find_smart_loop_surfs $free_surfs \
	    $mesh_size $sl_min_edge $mesh_size $sl_min_surf_area 1 \
	    $sl_angle $sl_max_curvature $exclusion_surfs]

	set free_surfs [scrib_lib_utils_remove_from_grouped $smart_loops $free_surfs]

	set smart_loops [scrib_lib_utils_list_olists_combine_dup_items $smart_loops]

	set i 0
	foreach loop $smart_loops {
	    incr i
	    set ll ""
	    foreach s $loop {set ll [concat $ll surface $s] }
	    ic_geo_subset_add_items SMART_LOOP/GROUP$i $ll
	}
	#
	#================ FIND/GROUP SLIVER SURFACES =========
	#
	if {$thin_surfs == 1} {
	    mess "Finding sliver surfaces...\n\n"
    
	    set thin_loops  [scrib_lib_feature_eval_combine_thin_surfaces $free_surfs  \
		$sl_aspect_ratio $sl_min_edge $sl_angle $sl_max_curvature ]
    
	    if {$thin_loops != ""} {
		mess "thin_loops are $thin_loops\n"
		set free_surfs [scrib_lib_utils_remove_from_grouped $thin_loops $free_surfs]    
	
		foreach tlp $thin_loops {
		    incr i
	   	    set ll ""
		    foreach s $loop {set ll [concat $ll surface $s] }
		    ic_geo_subset_add_items THIN_GROUP/GROUP$i $ll
		}
 	   }

	   
	}
	if {$smart_loops != ""} { mess "smart loops are $smart_loops\n" green }
    }
    set groups [concat $butt_loops $smart_loops $wider_groups $thin_loops $flange_loops]
mess "groups are $groups\n"
    foreach g $groups {
	set curves ""
	foreach s $g {
mess "g is $g\n"
	    set curves [concat $curves [ic_geo_boundary surface $s 0 1]]
	}
mess "curves is $curves\n"
	set points ""
	foreach c [lsort -unique [scrib_lib_utils_list_get_duplicate $curves]] {
	    ic_geo_set_dormant_entity curve $c 1
	    set points [concat $points [ic_geo_boundary curve $c 0]]
	}	
	foreach p $points {
	    if ![llength [ic_geo_incident point $p 0]] {
		ic_geo_set_dormant_entity point $p 1
	    }
	}	
    }
}


proc sup {} {
scrib_lib_utils_source_files {feature flanges fillet geo uns}
    foreach l [ic_geo_subset_list] {::AIEnv::GUIManagement::Tree::AddGroup geom_subset $l 1}
}


















