# this file is FAR from done. i need to check it in for ai_env gui.
# i'm not confident in a whole lot of it...

#
# Stuff for handling meshing directives.
# Author: Jason Aftosmis, 2002
#

## <h2>Meshing Directives</h2>

proc md_debug {s} {
    return
    mess "MD_DEBUG: $s\n" red
}


## Get the list of internal directive names

proc ic_meshdirect_available_directives_get {} {
    return [ic_meshdirect_sub_available_directives_get]
}
set dont_record_command(ic_meshdirect_available_directives_get) 1


##

proc ic_meshdirect_get_icon_direction {directive source_family target_family {source_type ""} \
					   {target_type ""}} {
    
    set projection_problem 0
    set vector "0 0 0"
    
    switch -- $directive {
	spotweld {
	    # foreach point in the source_family, project to all surfaces in the target_family
	    foreach pt [ic_geo_get_objects point $source_family] {
		set ptloc [ic_geo_get_point_location $pt]
		if {"" == $ptloc} { set projection_problem 1 ; continue }
		set target_surfaces [ic_geo_get_objects surface $target_family]
		if {"" == $target_surfaces} { set projection_problem 1 ; continue }
		set projloc [ic_geo_project_point surface $target_surfaces $pt]
		if {"" == $projloc} { set projection_problem 1 ; continue }
		set tvec [ic_geo_vec_diff $ptloc $projloc]
		set vector [ic_geo_vec_sum $vector $tvec]
	    }
	}
	seamweld {
	    # foreach curve in the source_family, project to all surfaces in the target_family
	    foreach curve [ic_geo_get_objects curve $source_family] {
		set coords [lindex [ic_geo_get_text_point curve $curve] 0]
		if {"" == $coords} { set projection_problem 1 ; continue }
		set target_surfaces [ic_geo_get_objects surface $target_family]
		if {"" == $target_surfaces} { set projection_problem 1 ; continue }
		set projloc [ic_geo_project_coords surface $target_surfaces $coords]
		if {"" == $projloc} { set projection_problem 1 ; continue }
		set tvec [ic_geo_vec_diff $coords $projloc]
		set vector [ic_geo_vec_sum $vector $tvec]
	    }
	}
	connector {
	    foreach ent [ic_geo_get_objects $source_type $source_family] {
		set coords [lindex [ic_geo_get_text_point $source_type $ent] 0]
		if {"" == $coords} { set projection_problem 1 ; continue }
		set target_ents [ic_geo_get_objects $target_type $target_family]
                if {"" == $target_ents} { set projection_problem 1 ; continue }
		set projloc [ic_geo_project_coords $target_type $target_ents $coords]
		if {"" == $projloc} { set projection_problem 1 ; continue }
		set tvec [ic_geo_vec_diff $coords $projloc]
		set vector [ic_geo_vec_sum $vector $tvec]
	    }
	}
	boltspider { }
	default {
	    mess "Error: don't know how to get icon vector for $directive\n" red
	}
    }
    
    if $projection_problem {
	mess "Warning: may not be able to accurately depict icons for \"$directive\" on \"$source_family\"\n" red
    }
    
    set nvec [ic_geo_vec_nrm $vector]

    return $nvec
}
set dont_record_command(ic_meshdirect_get_icon_direction) 1

##

proc ic_meshdirect_icons_visibility {family vis} {
    
    set directive [ic_meshdirect_get_directive_of_fam $family]
    if {"" == $directive} { return }
    set bctype "mesh_direct_$directive"
    #mess "ic_boco_set_family_visible $family $bctype $vis\n" purple
    ic_boco_set_family_visible $family $bctype $vis
}
set dont_record_command(ic_meshdirect_icons_visibility) 1

## 

proc ic_meshdirect_add_icon {directive source_family {target_family ""} {source_type ""} \
				 {target_type ""}} {
    
    set type "mesh_direct_$directive"
    set icontype [ic_meshdirect_sub_param_for_tag $directive "icontype"]
    if {"" == $icontype} {
	mess "could not get icontype for $directive; using arrow..." red
	set icontype "arrow"
    }
    set iconparams [ic_meshdirect_sub_param_for_tag $directive "iconparams"]
    if {"" == $iconparams} {
	mess "could not get iconparams for $directive; using ..." red
	return 0
    }
    #
    # --> general/default params
    #
    set lcs 0 ;# no local-coordinate-system
    set locations "geometry_or_mesh"
    set scale 3.0
    set maxnum 0
    set color "family_color"
    set vis 1   
    #
    # --> specific params
    #
    switch $icontype {
	arrow - barbell {
	    # these may not be given as args if we are loading a new tetin and displaying icons
	    # rather than creating new directives from a form. --jka
	    if {"" == $target_family} {
		set target_family [ic_meshdirect_directive_get_val_by_tag $directive $source_family "target_fam"]
	    }
	    if {"" == $source_type} {
		set source_type [ic_meshdirect_directive_get_val_by_tag $directive $source_family "source_type"]
	    }
	    if {"" == $target_type} {
		set target_type [ic_meshdirect_directive_get_val_by_tag $directive $source_family "target_type"]
	    }
	    # get the direction of the source part to the target part	    
	    set dir [ic_meshdirect_get_icon_direction $directive $source_family $target_family \
			 $source_type $target_type]
	    set params "[list $dir] $lcs $iconparams"
	}
	circle - ecks {
	    set params $iconparams	   
	}
    }
    switch $directive {
	spotweld - seamweld {
	    set color "red"
	}
	boltspider {
	    set color "blue"
	}
	connector {
	    ; # set color "white"
	}
    }
    
    set location_cmd [ic_meshdirect_sub_param_for_tag $directive "iconlocfunc"]
    if {"" != [info commands $location_cmd]} {
	set locations [$location_cmd $source_family]
	#
	}
    return [ic_boco_add_icon $type $source_family $icontype $locations $scale $maxnum \
		$color "" $params $vis ""]
}

## Remove the icons for a particular family/bctype combo

proc ic_meshdirect_delete_icon {directive families} {
    
    set type "mesh_direct_$directive"
    foreach family $families {
	ic_boco_delete_icons_for_bctypes $family $type
    }
}

## Remove the icons for entities if deleted

proc ic_meshdirect_delete_icon_on_ents {type ents} {
    set fams ""
    foreach ent $ents {
        if {"" == [ic_geo_check_objects_exist $type $ent] } continue
        lappend fams [ic_geo_get_family $type $ent]
    }
    if {[llength fams]} {
        foreach fam $fams {
            set dire [ic_meshdirect_get_directive_of_fam $fam]
            if {$dire != ""} {
                ic_meshdirect_delete_icon $dire $fam
            }
        }
    }
}

# ----> Arbitrary connectors


## Higher-level interface for setting meshing directives for arbitrary connectors

proc ic_meshdirect_arbitrary_connector_set {source_fams source_type \
	target_fam target_type result_fam {extra_args ""}} {
    
    set extra_targ_fams ""
    set target_fams $target_fam
    if {1 < [llength $target_fams]} {
	set ind 0
	set target_fam ""
	foreach tf $target_fams {
	    if {"" != $tf} {
		if {0 < $ind}  {
		    append extra_targ_fams "md_connector_target_fam$ind $tf "
		} else {
		    set target_fam $tf
		}
		incr ind
	    }
	}
    }
    
    set directive connector
    set status 0
    set reqargs [ic_meshdirect_sub_param_for_tag $directive "reqargs"]
    foreach arg $reqargs {
	set param [ic_meshdirect_sub_param_for_tag $directive $arg]
	set val [set $arg]
	#md_debug "ic_meshdirect_sub_params_set $source_fams $param $val"
	ic_meshdirect_sub_params_set $source_fams $param $val
    }
    
    ic_meshdirect_sub_extra_args_set $source_fams $extra_args
    ic_meshdirect_sub_extra_args_set $source_fams $extra_targ_fams

    ic_meshdirect_add_icon $directive $source_fams $target_fam $source_type $target_type
    
    return $status
}

## ----> Bolt spiders / bolt holes
##

## Higher-level interface for setting meshing directives for bolt-holes/-spiders

proc ic_meshdirect_boltspider_set {bolthole_fams result_fam {extra_args ""}} {
    
    set directive boltspider
    set status 0
    
    set param [ic_meshdirect_sub_param_for_tag $directive "result_fam"]
    set val $result_fam
    ic_meshdirect_sub_params_set $bolthole_fams $param $val
    
    #md_debug "extra_args: $extra_args"
    ic_meshdirect_sub_extra_args_set $bolthole_fams $extra_args
    
    ic_meshdirect_add_icon $directive $bolthole_fams

    return $status
}

## fixme

proc ic_meshdirect_boltspider_set_mesh_sizes {bolthole_fams} {

    set pi 3.14159265359
    
    foreach c [ic_geo_get_objects curve $fam] {
	set len [ic_geo_crv_length $c]
	set diameter [expr $len / $pi]
	
	#set emax [expr round ( $pi * $diameter / ( $Msize * pow (( 1.0 + $ratio * sin( 2 * $pi / $nelems)), $nrings)))]
	
	ic_set_meshing_params curve $c emax $emax
    }
}

## ----> Seam welding
##

## Higher-level interface for setting meshing directives for seam welding

proc ic_meshdirect_seamweld_set {source_fams target_fam result_fam {extra_args ""}} {
    #
    # --> Handle the tetin parameters
    #    
    set extra_targ_fams ""
    set target_fams $target_fam
    if {1 < [llength $target_fams]} {	
	set ind 0
	set target_fam ""
	foreach tf $target_fams {
	    if {0 < $ind}  {
		append extra_targ_fams "md_seamweld_target_fam$ind $tf "
	    } else {
		set target_fam $tf
	    }
	    incr ind
	}
    }
    
    set directive seamweld
    set status 0
    set reqargs [ic_meshdirect_sub_param_for_tag $directive "reqargs"]
    foreach arg $reqargs {
	set param [ic_meshdirect_sub_param_for_tag $directive $arg]
	set val [set $arg]
	ic_meshdirect_sub_params_set $source_fams $param $val
    }
    
    ic_meshdirect_sub_extra_args_set $source_fams $extra_args
    ic_meshdirect_sub_extra_args_set $source_fams $extra_targ_fams
    #
    # --> Handle the tetin modifications based on "md_seamweld_method"
    #
    foreach {n v} $extra_args {
	set $n $v
    }
    if [info exists md_seamweld_method] {
	
	switch -- $md_seamweld_method {
	    "standard" - "curtain_quad" {
		if ![ic_uns_is_loaded] {
		    set shadow_args "-source_family $source_fams -target_families $target_fam"
		    # maybe also -max_projection $maxProjection
		    if [catch {eval ic_meshdirect_seam_weld_shadow_curves_create $shadow_args} res] {
			mess "$res\n" red
		    }
		    if {$res == ""} {
		        mess "Error: cannot do seam-weld\n" red
		        return 1
		    }
		} else {
		    set source_curves [ic_geo_get_objects curve   $source_fams]
		    set target_surfs  [ic_geo_get_objects surface $target_fam]
		    ic_meshdirect_enforce_curve_shadow_nodes_on_surfaces $source_curves $target_surfs
		}
	    }
	    "glinka" {
		if 0 {
		    # not now we don't, not anymore
		    ic_meshdirect_seam_weld_glinka_geo_create $source_fams $target_fam $extra_args
		}
	    }
	    default {
		mess "cannot deal with seam-weld method \"$md_seamweld_method\"\n" red
		return 1
	    }
	}
    }

    ic_meshdirect_add_icon $directive $source_fams $target_fam
    
    return $status
}

## ----> Spot welding
##

## Higher-level interface for setting meshing directives for spot welding

proc ic_meshdirect_spotweld_set {source_fams target_fam result_fam {extra_args ""}} {
        	    
    set extra_targ_fams ""
    set target_fams $target_fam
    if {1 < [llength $target_fams]} {	
	set ind 0
	set target_fam ""
	foreach tf $target_fams {
	    if {0 < $ind}  {
		append extra_targ_fams "md_spotweld_target_fam$ind $tf "
	    } else {
		set target_fam $tf
	    }
	    incr ind
	}
    }
    
    set directive spotweld
    set status 0
    set reqargs [ic_meshdirect_sub_param_for_tag $directive "reqargs"]
    foreach arg $reqargs {
	set param [ic_meshdirect_sub_param_for_tag $directive $arg]
	set val [set $arg]
	ic_meshdirect_sub_params_set $source_fams $param $val
    }
    
    #xxx check for method to make it non-verbose ?
    
    ic_meshdirect_sub_extra_args_set $source_fams $extra_args
    ic_meshdirect_sub_extra_args_set $source_fams $extra_targ_fams
    
    ic_meshdirect_add_icon $directive $source_fams $target_fam

    return $status
}

## get the verbose list of current possible types of spotwelds
## this is good for getting a list of methods for a gui each
## element of which ic_meshdirect_* procs will recognize later.

proc ic_meshdirect_spotweld_types_verbose_list {} {
    global meshdirect_spotweldtypes
    
    # make the "firstentry" hash key come first: "standard" here
    set firstentry standard
    
    if {[info exists firstentry] && ("" != $firstentry)} {
	if [info exists meshdirect_spotweldtypes($firstentry)] {
	    lappend types $meshdirect_spotweldtypes($firstentry)
	}
    }
    
    foreach t [array names meshdirect_spotweldtypes] {
	
	if {[info exists firstentry] && ("" != $firstentry)} {
	    # skip firstentry (standard) if there
	    if [string equal $t $firstentry] { continue }
	}
	
	lappend types $meshdirect_spotweldtypes($t)
    }
    
    if ![info exists types] { return "" }
    
    return $types
}
set dont_record_command(ic_meshdirect_spotweld_types_verbose_list) 1

## returns the meshing_directive val for the given verbose
## human text which should be an element of the list given
## by ic_meshdirect_spotweld_types_verbose_list. i use this
## in ai_env.

proc ic_meshdirect_spotweld_types_verbose_get {param} {
    global meshdirect_spotweldtypes
    
    foreach t [array names meshdirect_spotweldtypes] {
	if [string equal $param $t] {
	    return $meshdirect_spotweldtypes($t)
	}
    }
    # just in case it's not verbose...
    foreach t [array names meshdirect_spotweldtypes] {
	if [string equal $meshdirect_spotweldtypes($t) $param] {
	    return $meshdirect_spotweldtypes($t)
	}    
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_spotweld_types_verbose_get) 1

## get the verbose list of current possible types of spotwelds
## this is good for getting a list of methods for a gui each
## element of which ic_meshdirect_* procs will recognize later.

proc ic_meshdirect_seamweld_types_verbose_list {} {
    global meshdirect_seamweldtypes
    
    # make the "firstentry" hash key come first: "standard" here
    set firstentry standard
    
    if {[info exists firstentry] && ("" != $firstentry)} {
	if [info exists meshdirect_seamweldtypes($firstentry)] {
	    lappend types $meshdirect_seamweldtypes($firstentry)
	}
    }
    
    foreach t [array names meshdirect_seamweldtypes] {
	
	if {[info exists firstentry] && ("" != $firstentry)} {
	    # skip firstentry (standard) if there
	    if [string equal $t $firstentry] { continue }
	}
	
	lappend types $meshdirect_seamweldtypes($t)
    }
    
    if ![info exists types] { return "" }
    
    return $types
}
set dont_record_command(ic_meshdirect_seamweld_types_verbose_list) 1

## returns the meshing_directive val for the given verbose
## human text which should be an element of the list given
## by ic_meshdirect_seamweldtypes_types_verbose_list. i use this
## in ai_env.

proc ic_meshdirect_seamweld_types_verbose_get {param} {
    global meshdirect_seamweldtypes
    
    foreach t [array names meshdirect_seamweldtypes] {
	if [string equal $param $t] {
	    return $meshdirect_seamweldtypes($t)
	}
    }
    # just in case it's not verbose...
    foreach t [array names meshdirect_seamweldtypes] {
	if [string equal $meshdirect_seamweldtypes($t) $param] {
	    return $meshdirect_seamweldtypes($t)
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_seamweld_types_verbose_get) 1

## returns the meshing_directive tag for the given verbose
## human text which should be an element of the list given
## by ic_meshdirect_spotweld_types_verbose_list. i use this
## in ai_env.

proc ic_meshdirect_spotweld_types_param_get {verbose_type} {
    global meshdirect_spotweldtypes
    
    foreach t [array names meshdirect_spotweldtypes] {
	#md_debug "$meshdirect_spotweldtypes($t) && $verbose_type"
	if [string equal $meshdirect_spotweldtypes($t) $verbose_type] {
	    return $t
	}
    }
    # just in case it's not verbose...
    foreach t [array names meshdirect_spotweldtypes] {
	if [string equal $t $verbose_type] {
	    return $t
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_spotweld_types_param_get) 1

## returns the meshing_directive tag for the given verbose
## human text which should be an element of the list given
## by ic_meshdirect_seamweld_types_verbose_list. i use this
## in ai_env.

proc ic_meshdirect_seamweld_types_param_get {verbose_type} {
    global meshdirect_seamweldtypes
    
    foreach t [array names meshdirect_seamweldtypes] {
	#md_debug "$meshdirect_seamweldtypes($t) && $verbose_type"
	if [string equal $meshdirect_seamweldtypes($t) $verbose_type] {
	    return $t
	}
    }
    # just in case it's not verbose...
    foreach t [array names meshdirect_seamweldtypes] {
	if [string equal $t $verbose_type] {
	    return $t
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_seamweld_types_param_get) 1


## get the verbose list of current possible splitting options for spotwelds
## this is good for getting a list of methods for a gui each
## element of which ic_meshdirect_* procs will recognize later.

proc ic_meshdirect_weld_splitting_verbose_list {} {
    global meshdirect_weldsplitting
    
    foreach t [array names meshdirect_weldsplitting] { 
	lappend types $meshdirect_weldsplitting($t)
    }
    
    if ![info exists types] { return "" }
    
    return $types
}
set dont_record_command(ic_meshdirect_weld_splitting_verbose_list) 1

## returns the meshing_directive tag for the given verbose
## human text which should be an element of the list given
## by ic_meshdirect_spotweld_types_verbose_list. i use this
## in ai_env.

proc ic_meshdirect_weld_splitting_param_get {verbose_type} {
    global meshdirect_weldsplitting
    
    foreach t [array names meshdirect_weldsplitting] {
	if [string equal $meshdirect_weldsplitting($t) $verbose_type] {
	    return $t
	}
    }
    # just in case verbose is not verbose
    foreach t [array names meshdirect_weldsplitting] {
	if [string equal $t $verbose_type] {
	    return $t
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_weld_splitting_param_get) 1

##

proc ic_meshdirect_weld_splitting_verbose_get { param } {
    global meshdirect_weldsplitting
    
    foreach t [array names meshdirect_weldsplitting] {
	if [string equal $param $t] {
	    return $meshdirect_weldsplitting($t)
	}
    }
    # just in case it's not verbose...
    foreach t [array names meshdirect_weldsplitting] {
	if [string equal $meshdirect_weldsplitting($t) $param] {
	    return $meshdirect_weldsplitting($t)
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_weld_splitting_verbose_get) 1

##

proc ic_meshdirect_glinka_side_param_get {verbose_type} {
    global meshdirect_glinkasides
    
    foreach t [array names meshdirect_glinkasides] {
	if [string equal $meshdirect_glinkasides($t) $verbose_type] {
	    return $t
	}
    }
    # just in case verbose is not verbose
    foreach t [array names meshdirect_glinkasides] {
	if [string equal $t $verbose_type] {
	    return $t
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_glinka_side_param_get) 1

## To get verbose glinka subtypes

proc ic_meshdirect_glinka_subtype_verbose_get {param} {
    global meshdirect_glinkasubtypes
    foreach t [array names meshdirect_glinkasubtypes] {
        if [string equal $param $t] {
            return $meshdirect_glinkasubtypes($t)
        }
    }
    # just in case its not verbose
    foreach t [array names meshdirect_glinkasubtypes] {
        if [string equal $meshdirect_glinkasubtypes($t) $param] {
            return $meshdirect_glinkasubtypes($t)
        }
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_glinka_subtype_verbose_get) 1

##

proc ic_meshdirect_glinka_side_verbose_get { param } {
    global meshdirect_glinkasides
    
    foreach t [array names meshdirect_glinkasides] {
	if [string equal $param $t] {
	    return $meshdirect_glinkasides($t)
	}
    }
    # just in case it's not verbose...
    foreach t [array names meshdirect_glinkasides] {
	if [string equal $meshdirect_glinkasides($t) $param] {
	    return $meshdirect_glinkasides($t)
	}
    }
    return "__none__"
}
set dont_record_command(ic_meshdirect_glinka_side_verbose_get) 1

## this is for use by med_gui/ai_env.

proc ic_meshdirect_directive_set_vals_by_tag {directive fams vals} {
    
    # this vals list must be in the order of tags
    set tags [ic_meshdirect_directive_tags_get $directive]
    foreach tag $tags val $vals {
	ic_meshdirect_directive_set_val_by_tag $directive $fams $tag $val
    }
    return 1 
}

## this is for use by med_gui/ai_env

proc ic_meshdirect_directive_set_val_by_tag {directive fams tag val} {
    
    if {"" == $val} { return 0 }
    
    set param [ic_meshdirect_sub_param_for_tag $directive $tag]
    if {"" == $param} {
	#mess "could not get param for $directive tag $tag\n" red
	return 1
    }
    
    set stat 0
    foreach f $fams {
	ic_geo_set_family_params $f $param $val
    }
    return $stat
}

## this is for use by med_gui/ai_env

proc ic_meshdirect_directive_get_val_by_tag {directive fam tag} {
    
    set param [ic_meshdirect_sub_param_for_tag $directive $tag]
    if {"" == $param} {
	#mess "could not get param for $directive tag $tag\n" red
	return ""
    }
    if { ($fam == "ORFN") || ($fam == "VORFN") } {
	return ""
    }
    if  ![ic_geo_check_family $fam] {
	#mess "cannot find family $fam in the geometry loaded!\n" red
	return ""
    }
    return [ic_geo_get_family_param $fam $param]
}
set dont_record_command(ic_meshdirect_directive_get_val_by_tag) 1

proc ic_meshdirect_target_fam_params_get {directive} {
    
    set target_fam_base [ic_meshdirect_sub_param_for_tag $directive target_fam]
    lappend paramlist $target_fam_base
    set ind 1
    # this "20" is horrible, but.. --jka
    while {$ind < 20} {
	lappend paramlist "${target_fam_base}${ind}"
	incr ind
    }
    return $paramlist
}
set dont_record_command(ic_meshdirect_target_fam_params_get) 1


## Does the currently loaded tetin have meshing directives defined.

proc ic_meshdirect_model_has_directives { } {
    foreach f [ic_geo_list_families] {
	if {"" != [ic_meshdirect_get_directive_of_fam $f]} {
	    return 1
	}
    }
    return 0
}
set dont_record_command(ic_meshdirect_model_has_directives) 1


## return the meshing directive defined on a family, or the first found for a
## list of families.

proc ic_meshdirect_get_directive_of_fam {famnames} {
    foreach f $famnames {
	foreach d [ic_meshdirect_available_directives_get] {
	    # set stuff [ic_meshdirect_get $d $famname]
	    # no, but every directive has such a tag
	    set stuff [ic_meshdirect_directive_get_val_by_tag $d $f active]
	    if {"" != $stuff} {
		#puts "part_family \"$f\" has directive(s)"
		return $d
	    }
	}
    }
    #mess "couldn't determine directive of given part $partname\n"
    return ""
}
set dont_record_command(ic_meshdirect_get_directive_of_fam) 1

## return whether or not the given family is used in any meshing directives
# this could be horribly slow

proc ic_meshdirect_directive_uses_family {given_fam} {
    foreach f [ic_geo_list_families] {
	foreach d [ic_meshdirect_available_directives_get] {	
	    foreach p [ic_meshdirect_target_fam_params_get $d] {
		if [string equal "*_fam" $p] {
		    set fam [ic_meshdirect_directive_get_val_by_tag $d $f $p]
		    if [string equal $given_fam $fam] { return 1 }
		}
	    }
	}
    }
    return 0
}
set dont_record_command(ic_meshdirect_directive_uses_family) 1

## return whether or not the given family is used in any meshing directives
# this could be horribly slow

proc ic_meshdirect_param_requires_remeshing {} {
    foreach f [ic_geo_list_families] {
	foreach d [ic_meshdirect_available_directives_get] {
	    set val [ic_meshdirect_directive_get_val_by_tag $d $f "splitting"]
	    if {0 <= [string first "remesh" $val]} {
		if [ic_meshdirect_directive_get_val_by_tag $d $f active] {
		    return 1
		}
	    }
	}
    }
    return 0
}
set dont_record_command(ic_meshdirect_param_requires_remeshing) 1

proc ic_meshdirect_target_fam_vals_get {directive fam} {
    set param [ic_meshdirect_sub_param_for_tag $directive target_fam]
    if {"" == $param} {
	#mess "could not get param for $directive tag $tag\n" red
	return ""
    }
    if ![ic_geo_check_family $fam] {
	#mess "cannot find family $fam in the geometry loaded!\n" red
	return ""
    }
    set target_fams ""
    foreach par [ic_meshdirect_target_fam_params_get $directive] {
	set val [ic_geo_get_family_param $fam $par]
	if {"" != $val} {
	    lappend target_fams $val
	}
    }
    if {"" != $target_fams} {
	set target_fams [lsort -unique $target_fams]
    }
    return $target_fams
}
set dont_record_command(ic_meshdirect_target_fam_vals_get) 1

## this is for use by med_gui/ai_env

proc ic_meshdirect_directive_tags_get {directive} {
    # each directive should have a "tags" list in the array
    return [ic_meshdirect_sub_param_for_tag $directive "alltags"]
}
set dont_record_command(ic_meshdirect_directive_tags_get) 1

#----------------------------------------------------------------

## ----> Weld importing

## needs doc.

proc ic_meshdirect_spotweld_file_import {filename {args ""}} {
    
    # check for the scriblib scripting library...
    set spotwelding_import_cmd "scrib_lib_welding_spot_import_exec"
    if {"" == [info commands $spotwelding_import_cmd]} {
	mess "cannot import spotwelds via file import...missing procedure definitions in [info commands scrib_lib_*]\n" red	
	return -1
    }
    
    # mode is hard-coded; should be ok; fixme?
    set res [eval $spotwelding_import_cmd "vif" $filename $args]
    ic_uns_set_modified 1
    return $res
}

## --> Executing meshing directives

## Run meshing directives, using the given input mesh <em>idomain</em> and 
## <em>tetin</em> files and, optionally, the output mesh <em>odomain</em>.
## The full path names must be given.
## One or more directive arguments may be given.  These arguments are:
## <ul> 
## <li> <b>-spotweld</b>   : run the spotweld directives defined in the tetin
## <li> <b>-seamweld</b>   : run the seamweld directives defined in the tetin
## <li> <b>-boltspider</b> : run the boltspider directives defined in the tetin
## <li> <b>-connector</b>  : run the connector directives defined in the tetin
## <li> <b>-pml</b>        : run the pml directives defined in the tetin
## </ul>

proc ic_meshdirect_standalone_exec { idomain tetin {odomain ""} args } {
    global env
    
    if ![lindex [check_licenses_enabled] 0] {
	error "No med license was obtained"
    }
    
    if { ($idomain == "") || ($tetin == "") }  {
	mess "bad arguments to ic_run_meshdirect\n" red
	return 0
    }
    set prog $env(ICEM_ACN)/icemcfd/mesh_direct
    if { $odomain == $idomain} {
	set cmd "$prog $idomain $tetin $args"
    } else {
	set cmd "$prog $idomain $tetin $odomain $args"
    }
    mess "Running: $cmd\n"
    catch { eval exec $cmd } res
    mess "$res\n"
    
    if [file exists $odomain] { return 1 }
    mess "Error: meshing directives didn't create $odomain\n" red    
    return 0
}

## Run meshing directives, using the mesh and proj objects in memory.
## Any number of these directive types may be given:
## <ul> 
## <li> <b>spotweld</b>   : run the spotweld directives defined in the tetin
## <li> <b>seamweld</b>   : run the seamweld directives defined in the tetin
## <li> <b>boltspider</b> : run the boltspider directives defined in the tetin
## <li> <b>connector</b>  : run the connector directives defined in the tetin
## <li> <b>pml</b>        : run the pml directives defined in the tetin
## </ul>
## Additionally, options can be specified in <em>args</em> such as:
## <li> <b>-remesh_affected_elements</b>   <em>remesh elements affected by directive</em>

proc ic_meshdirect_object_exec {types {args ""}} {
    global uns_cur
    
    set status 0
    if {("" == $types) || ("all" == $types)} {
	set types [ic_meshdirect_available_directives_get]
    }
    
    if ![ic_uns_is_loaded] {
	mess "must have unstruct mesh loaded to execute meshing directives: $types!\n" red
	return $status
    }
    if ![ic_geo_is_loaded] {
	mess "must have geometry loaded to execute meshing directives: $types!\n" red
	return $status
    }
    
    # see if we are going to be doing remeshing; not if glinka, though
    set we_are_remeshing [expr {0 <= [string first "remesh" $args]}]
    if {0 <= [string first "glinka" $args]} {
	set we_are_remeshing 0
    }
    
    set md_args ""
    set merge_nodes 0
    foreach t $types {
	append md_args "-$t "
    if { ![string compare $t "seamweld"] } { set merge_nodes 1 }
    }
    eval append md_args $args
    
    # run mesh_direct
    
    busy 1
    md_debug "busy 1"
    mess "Running meshing directives $types...\n"
    set status [catch {eval $uns_cur run_mesh_direct $md_args} res]    
    # This is for glinka hypotenuse single edges - bug 11644 --rpai
    if {$merge_nodes} { ic_uns_merge_nodes all 0.0001 }
    
    if {"" != $res} { mess "$res\n" red }
    mess "Finished running meshing directives: $types\n"

    # maybe do some remeshing
    #
    # FOR NOW, DO NO REMESHING!! --JKA FIXME
    #
    set we_are_remeshing 0
    
    if $we_are_remeshing {
	
	if 1 {
	    set all_fams [ic_uns_list_families]
	    ic_uns_smooth_by_quality $all_fams {QUAD_4 TRI_3} {Quality}
	    ic_uns_change_type_by_quality $all_fams "QUAD_4" "TRI_3" "Quality" ".2"
	    ic_uns_smooth_by_quality $all_fams {QUAD_4 TRI_3} {Quality} ".2"
	    
	} else {
	    
	    # the old way using andreas' remesh stuff	    
	    ic_quad_remesh_bad_elements_by_quality [ic_uns_list_families] {QUAD_4 TRI_3} {Quality}	    
	}
	
	# check for overlapping elements because of quad remeshing problems
	
	set ntries 4
	set still_bad 1
	set fams_for_remeshing [ic_uns_list_families]
	for {set i 0} {($i < $ntries) && ($still_bad)} {incr i} {
	    # func ic_quad_remesh_by_diagnostic will return "nproblematic elements, 0 if it was already ok.
	    set still_bad [ic_quad_remesh_by_diagnostic overlap $fams_for_remeshing {TRI_3 QUAD_4} 2]
	}
    }
    
    busy 0
    md_debug "busy 0"
    
    return $status
}

## Create a standard (BAR/LINE) weld from the prescribed point <em>ppname</em>
## in source family <em>source_fam</em>e to the target family <em>target_fam</em>,
## putting the weld in <em>weld_fam</em>.

proc ic_meshdirect_weld_prepoint_standard {ppname args} {
    global uns_cur
    
    set source_fam ""
    set target_fam ""
    set weld_fam ""
    foreach {n v} $args {
	set $n $v
    }
    if {("" == $source_fam) || ("" == $target_fam) || ("" == $weld_fam)} {
	mess "the given args $args appear to be insufficient...trying anyway.\n" red
    }
    #
    # run mesh_direct for the prepoint
    #
    set weldargs "$ppname $source_fam $target_fam $weld_fam"
    set status [catch {eval $uns_cur weld_prepoint_standard $weldargs} res]
    
    if {"" != $res} { mess "$res\n" red }
    
    return $status
}

## Create a weld of some type from the prescribed point <em>ppname</em>
## in source family <em>source_fam</em>e to the target family <em>target_fam</em>,
## putting the weld in <em>weld_fam</em>.

proc ic_meshdirect_weld_prepoint {ppname args} {
    global uns_cur    

    set method standard
    set radius 0
    set rbe3_fam "RBE3"
    set source_fam ""
    set target_fam ""
    set weld_fam ""
    foreach {n v} $args {
	set $n $v
    }
    if {("" == $source_fam) || ("" == $target_fam) || ("" == $weld_fam)} {
	mess "the given args $args appear to be insufficient...trying anyway.\n" red
    }
    #
    # run mesh_direct for the prepoint
    #
    switch -- $method {
	meshless_hex - meshless_area - meshless_bar {
	    set weldargs "$ppname $source_fam $target_fam $weld_fam $method $rbe3_fam $radius"
	    set status [catch {eval $uns_cur weld_prepoint_meshless $weldargs} res]
	}
	default {
	    # including standard
	    set weldargs "$ppname $source_fam $target_fam $weld_fam"
	    set status [catch {eval $uns_cur weld_prepoint_standard $weldargs} res]
	}
    }
    
    if {"" != $res} { mess "$res\n" red }
    
    return $status
}

## Create a spot weld using the following flags as arguments:
##
## One of these methods for source point location:
##   @   -location          {%F %F %F}          coords of location to weld at
##   @   -prepoint          %S                  name of prepoint of location to weld at
## 
## Kind of weld to create there:
##   @   -method            %S                  one of { "standard",      "meshless_hex",
##                                                       "meshless_area", "meshless_bar",
##                                                       "shadow" }
## (default is "standard")
##
## A bit more on how to create it:
##   @   -maxprojection     %F                  maximum length of weld
## (default is infinity)
##
##   @   -element_splitting %S                  one of { "terminate"       , "propagate" ,
##                                                       "remesh_all_quad" , "remesh_tri_quad" };
## (default is "terminate")
##
## If it's an AreaWeld, this is how you specify how large an area:
##   @   -radius            %F                  radius of area_weld coverage
##
## This is how you manage the parts/families which it connects:
##   @   -target_family     %S                  family to which we should weld
##   @   -source_family     %S                  family from which we should weld (syn. of -target_family)
## NB that target can have multiple definitions, each of which will be targetted.
##
## What to do with new elements:
##   @   -spotweld_family   %S                  family for weld elements to go into
##   @   -rbe3_family       %S                  family for rbe3 element to go into

proc ic_meshdirect_spot_weld {args} {
    global uns_cur
    
    # --> check "method" to see whether we are creating shadow points,
    #     for which operation no mesh need be loaded
    
    foreach {n v} $args {
	switch -- $n {
	    "-method" {
		if {"shadow" == $v} {
		    # this does no proceed as normally--it just uses a tetin
		    return [ic_meshdirect_spot_weld_shadow $args]
		}
	    }
	}
    }
    
    # --> we are not creating shadow points, so carry on as normal,
    #     for which a mesh must be loaded

    if ![ic_uns_is_loaded] {
	error "Must have unstruct mesh loaded to spot weld by method \"$method\""
    }
    
    set status [catch {eval $uns_cur spot_weld $args} res]
    
    if {(0 >= $status) && ("" != $res)} {
	mess "$res\n" red
    }
    
    return $status
}

## Create shadow points for the given args.

proc ic_meshdirect_spot_weld_shadow_points_create {args} {
    
    if ![ic_geo_is_loaded] {
	mess "Error: must have geometry loaded to create seam weld shadow curves\n" red
	return ""
    }

    foreach {n v} $args {
	switch -- $n {
	    "-source_part" {
		foreach fam [ic_boco_get_fams_of_part $v] {
		    mess "for part $v, got fam $fam\n" red
		    if [ic_geo_check_family $fam] {
			set source_points [ic_geo_get_objects point $fam]
		    } else {
			mess "Warning: could not find source family \"$fam\" in the geometry\n" red		       
		    }
		}
	    }
	    "-source_family" {
		foreach fam $v {
		    if [ic_geo_check_family $fam] {
			set source_points [ic_geo_get_objects point $fam]
		    } else {
			mess "Warning: could not find source family \"$fam\" in the geometry\n" red		       
		    }
		}
	    }
	    "-target_part" - "-target_parts" {
		foreach part $v {
		    set fams [ic_boco_get_fams_of_part $part]
		    eval lappend target_families $fams
		}
	    }
	    default {
		# including: source_points, target_families, max_projection
		set [string range $n 1 end] $v
	    }
	}
    }
    if ![info exists source_points]  {
        mess "Error: could not get source points to create spot weld shadow points\n" red
        return ""
    }
    if ![info exists target_families]  {
        mess "Error: could not get target families to create spot weld shadow points\n" red
        return ""
    }
    if ![info exists max_projection] { set max_projection "-1" }

    set shadow_points_created ""
    
    foreach pnt $source_points {
	#
	#
	set src_pt_loc [ic_geo_get_point_location $pnt]
	#
	# --> foreach target part, project to its list of surfaces
	#
	foreach trg_fam $target_families {	    
	    set target_surfaces [ic_geo_get_objects surface $trg_fam]
	    
	    #   project the point each of the target_surfaces
	    #   see which of the surface_surfaces is closest
	    
	    set near_surf   ""
	    set near_dist   ""
	    set near_coords ""
	    foreach srf $target_surfaces {
		set proj_coords [ic_geo_project_point surface $srf $src_pt_loc {0 0 0}]
		set this_dist [ic_geo_pnt_dist $proj_coords $src_pt_loc]		
		if {(0 < $max_projection) && ($max_projection < $this_dist)} { continue }		
		# --> this surface is close enough, so see if this is the closest surface		
		if {($near_dist == "") || ($near_dist > $this_dist)} {
		    set near_surf $srf
		    set near_dist $this_dist
		    set near_coords $proj_coords
		}
	    }
	    if {$near_surf == ""} continue	    
	    set targ_pt_family [ic_meshdirect_spot_weld_shadow_point_family $near_surf]
	    set targ_pt_name   [ic_meshdirect_spot_weld_shadow_point_name $near_surf $pnt]
	    ic_geo_cre_pnt $targ_pt_family $targ_pt_name $near_coords
	    ic_geo_add_embedded_pnt $near_surf $targ_pt_name
	    lappend shadow_points_created $targ_pt_name
	}
    }
    return $shadow_points_created
}

##

proc ic_meshdirect_entity_is_shadow_entity {ent_name} {
    
    # note the "*" used for the regexp below
    set shadow_pattern [ic_meshdirect_shadow_name_suffix]
    
    # see if the curve name matches "shadow_pattern". if so, add it to return list
    # this regexp sucks as defined, but i'm having trouble with :
    #    couldn't compile regular expression pattern: quantifier operand invalid
    # today. this should be good enough for now. --jka
    return [regexp -- $shadow_pattern $ent_name]
}

## Get the shadow welds that have been created on the given surface <em>srf</em>.

# This won't work if the points have been moved between parts!!!! fixme jka

proc ic_meshdirect_spot_weld_shadow_points_on_surface {srf} {
    
    set srf_family     [ic_meshdirect_spot_weld_shadow_point_family $srf]
    
    # get all curvesin the same family as surface
    set all_points_in_srf_fam [ic_geo_get_objects point $srf_family]
    
    # from those, get those with the shadow suffix
    set shadow_points_on_surface ""
    foreach pnt $all_points_in_srf_fam {
	if [ic_meshdirect_entity_is_shadow_entity $pnt] {
	    lappend shadow_points_on_surface $pnt
	}
    }
    
    return $shadow_points_on_surface
}

## Create shadow curves in the geometry for the seam weld described in the args.
## Example args: "-source_family $locFamOnWhich -target_families $seamTargetFam"

proc ic_meshdirect_seam_weld_shadow_curves_create {args} {
    # fixme -- this is because we can't count on a good topo coming out of the
    # fixme -- creation of these curves. remove this return when schtuff works --jka
    #return ;# fixme
    #
    md_debug "args are $args\n"
    if ![ic_geo_is_loaded] {
        mess "Error: must have geometry loaded to create seam weld shadow curves\n" red
        return ""
    }
    #if [ic_uns_is_loaded] {
    # 	mess "Error: must create seam weld shadow curves before mesh is created\n" red
    # 	return ""
    #}
    #
    # --> parse up zee args: e.g. "-location" into variable "location"
    #
    foreach {n v} $args {
        switch -- $n {
        "-source_part" {
            foreach fam [ic_boco_get_fams_of_part $v] {
                mess "for part $v, got fam $fam\n" red
                if [ic_geo_check_family $fam] {
                    set source_curves [ic_geo_get_objects curve $fam]
                } else {
                    mess "Warning: could not find \
                    source family \"$fam\" in the geometry\n" red
                }
            }
        }
        "-source_family" {
            foreach fam $v {
                if [ic_geo_check_family $fam] {
                    set source_curves [ic_geo_get_objects curve $fam]
                } else {
                    mess "Warning: could not find \
                        source family \"$fam\" in the geometry\n" red
                }
            }
        }
        "-target_family" - "-target_families" {
            foreach fam $v {
                if [ic_geo_check_family $fam] {
                    set target_surfaces [ic_geo_get_objects surface $fam]
                } else {
                    mess "Warning: could not find \
                    target family \"$fam\" in the geometry\n" red
                }
            }
        }
        "-target_part" - "-target_parts" {
            foreach fam [ic_boco_get_fams_of_part $v] {
                if [ic_geo_check_family $fam] {
                    set target_surfaces [ic_geo_get_objects surface $fam]
                } else {
                    mess "Warning: could not find \
                    target family \"$fam\" in the geometry\n" red
                }
            }
        }
        default {
            # including: source_curves, target_surfaces, max_projection
            set [string range $n 1 end] $v
        }
        }
    }
    if {![info exists source_curves] || $source_curves == ""} {
        mess "Error: could not get source curves to create seam weld shadow curves\n" red
        return ""
    }
    if {![info exists target_surfaces] || $target_surfaces == ""} {
        mess "Error: could not get target surfaces to create seam weld shadow curves\n" red
        return ""
    }
    if ![info exists max_projection] { set max_projection "-1" }

    #
    #      (the return val is the list of new_curves)
    #    
    # --> foreach curve given, or foreach curve in given source_part
    
    set new_shadow_curves ""
    foreach crv $source_curves {
        set parent_surf [ic_geo_incident curve $crv]
        if {"" == $parent_surf} {
            mess "Error: skip unconnected source curve\n" red
            continue
        }
        # --> get the respresentative coordinates of the center of the curve
        #     Nio, just get any old point for now...fixme
        set crv_rep_coords [lindex [ic_geo_get_text_point curve $crv] 0]
        # [ic_geo_project_coords curve $crv {0 0 0} {0 0 0}]
        #   project the curve each of the target_surfaces
        #   see which of the surface_surfaces is closest
        set near_surfs ""
#        set near_dist ""
        foreach srf $target_surfaces {
            if {"" == $srf} { continue }
            set proj_coords [ic_geo_project_point surface $srf $crv_rep_coords {0 0 0}]
            set this_dist [ic_geo_pnt_dist $proj_coords $crv_rep_coords]
#            if {$near_dist == ""} { set near_dist $this_dist }
            if {(0 < $max_projection) && ($max_projection < $this_dist)} continue           
#            if {$near_dist < $this_dist} continue
#            set near_dist $this_dist
            lappend near_surfs $srf
        }
        if {"" == $near_surfs} {
            mess "Warning: could not get near surface for curve \"$crv\" from target surfaces\n" red
            continue
        }
        set prj_crv_name [ic_meshdirect_seam_weld_shadow_curve_name $srf $crv]
        set prj_crv_fam [ic_meshdirect_seam_weld_shadow_curve_family $srf]
        set new_crv_name [ic_geo_project_curve_to_surface $crv $near_surfs $prj_crv_name $prj_crv_fam]
        if {"" == $new_crv_name || ("" == [ic_geo_check_objects_exist curve $new_crv_name])} {
            mess "Error: could not create seam weld shadow of curve \"$crv\" on surface \"$near_surfs\"\n" red
        } else {
            mess "Created seam weld shadow of curve \"$crv\" called \"$new_crv_name\"\n"
            lappend new_shadow_curves $new_crv_name
            set source_of_shadow($new_crv_name) $crv
            # ?else consider? : 
            # ic_geo_create_surface_segment $near_surfs "curve" $new_crv_name
            #
            # puts "ic_geo_trim_surface $near_surfs $new_crv_name"
            # set crv_params [ic_get_meshing_params curve $crv]
            # set trim_crv_name 
            # [ic_geo_trim_surface $near_surfs $new_crv_name]
            # md_debug "trim_crv_name = $trim_crv_name\n"
            #
            # --> also copy over the meshing params from the source curve !!
            #
            set crv_params [ic_get_meshing_params curve $crv]
            md_debug "new curve is $new_crv_name\n"
            foreach {name val} $crv_params {
                ic_set_meshing_params curve $new_crv_name $name $val
            }
        }
    }
    md_debug "ic_meshdirect_seam_weld_shadow_curves_create: ic_build_topo with tolerance [ic_geo_default_topo_tolerance]"
    # "-no_concat" added for Onyx request 45335.
    ic_build_topo [ic_geo_default_topo_tolerance] -no_concat -quiet
    foreach c $new_shadow_curves {
        if {[ic_geo_check_objects_exist curve $c] == ""} continue
        if {[ic_geo_incident curve $c] == ""} {
             set opt [tk_dialog .shadow_curve "Seam weld shadow curve" \
                "Warning: Seam weld shadow curve \"$c\" is not embedded in any surface" "" 1 \
                "Continue" "Remove shadow curve" "Cancel"]
             switch -- $opt {
                -1 { return }
                0 { continue }
                1 {
                    set points [ic_geo_boundary curve $c 0 0 0]
                    ic_delete_geometry curve names $c
                    if {$points != ""} { ic_delete_geometry point names $points }
                    mess "Removed seam weld shadow curve \"$c\" and attached points\n"
                    continue
                }
                2 {
                    mess "Warning: skip creating shadow curves\n" red
                    foreach sc $new_shadow_curves {
                        if {[ic_geo_check_objects_exist curve $sc] == ""} continue
                        set points [ic_geo_boundary curve $sc 0 0 0]
                        ic_delete_geometry curve names $sc
                        if {$points != ""} { ic_delete_geometry point names $points }
                        set fam [lindex [split [ic_geo_get_family curve $source_of_shadow($sc)] :] 0]
                        if {$fam != ""} {
                            ic_geo_set_family curve $fam names $source_of_shadow($sc) 0
                            ic_geo_set_part curve $source_of_shadow($sc) $fam 0
                        }
                    }
                    return "" 
                }
            }
        }
    }
    md_debug "return = $new_shadow_curves"
    return $new_shadow_curves
}

## Get all of the shadow curves which have been created on the given surface <em>srf</em>.

# This won't work if the curve has been moved between parts!!!! fixme jka

proc ic_meshdirect_seam_weld_shadow_curves_on_surface {srf} {
    
    set srf_family     [ic_meshdirect_seam_weld_shadow_curve_family $srf]
    
    # get all curves in the same family as surface
    set all_curves_in_srf_fam [ic_geo_get_objects curve $srf_family]
    
    # from those, get those with the shadow suffix
    set shadow_curves_on_surface ""
    foreach crv $all_curves_in_srf_fam {
	if [ic_meshdirect_entity_is_shadow_entity $crv] {
	    lappend shadow_curves_on_surface $crv
	}
    }
    
    return $shadow_curves_on_surface
}

##

proc ic_meshdirect_enforce_curve_shadow_nodes_on_surfaces { source_curves target_surfaces } {
    
    # test args: ic_meshdirect_enforce_curve_shadow_nodes_on_surfaces part_1003/370e492 part_1004/398    
    # See Bug 7226 --jka

    if ![ic_uns_is_loaded] {
	error "must have mesh loaded to enforce curve shadow nodes on surfaces"
    }
    if ![ic_geo_is_loaded] {
	error "must have geometry loaded to enforce curve shadow nodes on surfaces"
    }
    
    # Gets the bar elements on the source curve, and order the verts of the bars
    
    set curve_vertex_map [ic_uns_subset_get_vertices_on_curves $source_curves]
    
    # Project those vert locations onto the target surface(s)
    
    set vert_map [ic_uns_subset_create]
    set node_el_map [ic_uns_subset_create]
    set dim 2 ;# fixme--watch this
    set fam "SHADOW_NODE_ELS" ;# fixme
    ic_uns_new_family $fam
    
    foreach element [ic_uns_subset_list_elements $curve_vertex_map] {
	
	if {"VERTEX" != [lindex $element 0]} { continue }
	set vert_num [lindex $element 1]
	set vert_loc [lindex $element 2]
	
	# Project this location to the target_surfaces
	# Create a vertex there, disconnected for now
	
	set projloc [ic_geo_project_coords surface $target_surfaces $vert_loc]
	ic_uns_create_vertex $vert_map $projloc $dim
	
	# Create a NODE element for the vertex to use, then enforce it
	# with Andreas' function ; element type of 2
    # in case of volume elements need to set make consistent flag on 
    if {[ic_count_elements tetra]} {
        set eltype 3
        set make_cons 1
    } else {
        set eltype 2
        set make_cons 0
    }
	if ![ic_uns_create_element NODE $fam $vert_map 0 $node_el_map 0] {
	    ic_enforce_vertex $node_el_map $eltype 0 0 0 -1 $make_cons
	} else {	
	    mess "Error: could not enforece vertex $vert_num on the target surfaces: [ic_uns_subset_list_elements $node_el_map]\n" red
	}
	
	ic_uns_subset_clear $vert_map
	ic_uns_subset_clear $node_el_map
    }
    
    ic_uns_subset_delete $vert_map
    ic_uns_subset_delete $node_el_map
}


## This proc will tell you whether or not the surface incident to <em>crv</em>
## is coplanar to all of the surfaces in <em>srfs</em>.

# fixme: move to geo...tcl when debugged

proc ic_geo_crv_parent_srf_is_coplanar_to_nearest {crv srfs} {
    
    set parent_surf [ic_geo_incident curve $crv]
    if {"" == $parent_surf} {
        mess "Error: could not get surface incident on curve $crv\n" red
        return 0
    }
    if {1 < [llength $parent_surf]} {
        #mess "Error: could not get unique parent surface for the given curve: $parent_surf\n" red
        return 0
    }
    
    #    get the norm of the surface at the midpoint of the curve
    
    set parent_loc [lindex [ic_geo_get_crv_pos ".51"       $crv] 0]


    set parent_nrm [lindex [ic_geo_get_srf_nrm ".49" ".51" $parent_surf] 0]
    
    set parent_norm [ic_geo_vec_nrm $parent_nrm]
    
    set nearest_surf ""
    set mindist 999999999
    foreach srf $srfs {
	
	set coords_proj [ic_geo_project_coords surface $srf $parent_loc {0 0 0}]
	if {"" == $coords_proj} {
	    continue
	}
	set dist [ic_geo_pnt_dist $parent_loc $coords_proj]
	if {$dist < $mindist} {
	    set nearest_surf $srf
	    set mindist      $dist
	}
    }
    
    # get the normal of the target surface
    
    set target_norm [ic_geo_vec_nrm  [lindex [ic_geo_get_srf_nrm ".49" ".51" $nearest_surf] 0]]
    
    # return now if not parallel (dot is not within .05 of {1, -1}
    
    set dot_prod [ic_geo_vec_dot $parent_norm $target_norm]
    if {(.95 > $dot_prod) && (-.95 < $dot_prod)} {
	return 0
    }
    
    return 1
}
set dont_record_command(ic_geo_crv_parent_srf_is_coplanar_to_nearest) 1

#following copied from proc ic_meshdirect_seam_weld_glinka_geo_create_simplified & simplified
#by replacing auto-geometry modifications by checks for closed geometry --anand

proc ic_meshdirect_seam_weld_glinka_geo_create_simplified { source_curves target_family extra_args } {
    #
    if ![ic_geo_is_loaded] {
        mess "Error: must have geometry loaded to create seam weld shadow curves\n" red
        return ""
    }
# check for closed geometry & return null string if invalid --anand
	foreach c $source_curves {
		set ss [ic_geo_incident curve $c]
		if {[llength $ss] < 2} {
			mess "Error: Please close gap near Glinka Weld curve $c\n" red
			return ""
		}
		foreach s $ss {
			set connected([ic_geo_get_family surface $s]) 1
		}
	}
	foreach f $target_family {
		if ![info exists connected($f)] {
			mess "Error: Please close gap near Glinka Weld part [ic_boco_get_part_of_fam $f]\n" red
			return ""
		}
	}

    #
    # --> get the surfaces in the target family
    #    
    set target_surfaces ""
    foreach tf $target_family {
        if [ic_geo_check_family $target_family] {
            eval lappend target_surfaces [ic_geo_get_objects surface $target_family]
        } else {
            mess "Error: could not find target family \"$target_family\" in the geometry\n" red
        }
    }
    if {"" == $target_surfaces} {
        return ""
    }
    #
    # --> If mesh is already loaded, work directly on the mesh
    #
    if [ic_uns_is_loaded] {
        set target_surfs  [ic_geo_get_objects surface $target_family]
        ic_meshdirect_enforce_curve_shadow_nodes_on_surfaces $source_curves $target_surfs
        # ..?but still create geometry? --jka ?fixme? watchme at least
        #return
    }
    #
    # --> Get other args
    #    
    foreach {n v} $extra_args {
        set $n $v
    }
    if {(![info exists md_seamweld_maxprojection]) || ("" == $md_seamweld_maxprojection)} {
        set md_seamweld_maxprojection "-1" 
    }
    if {(![info exists md_seamweld_glinka_layers]) || ("" == $md_seamweld_glinka_layers)} {
        set md_seamweld_glinka_layers "2"
    }
    if {(![info exists md_seamweld_extend_type]) || ("" == $md_seamweld_extend_type)} {
        set md_seamweld_extend_type "1"
    }

    #
    # --> Add params to the crv for the proper offset of the glinka layers
    #
    
    set scale [ic_geo_get_ref_size]
    if {$scale == ""} { set scale 1.0 }
    foreach tc $source_curves {
        if [info exists md_seamweld_glinka_height] {
            ic_set_meshing_params curve $tc ehgt [expr ($md_seamweld_glinka_height * 1.0) / $md_seamweld_glinka_layers]
        }
        if [info exists md_seamweld_glinka_height_ratio] {
            ic_set_meshing_params curve $tc erat $md_seamweld_glinka_height_ratio
        }
        if [info exists md_seamweld_glinka_layers] {
            ic_set_meshing_params curve $tc nlay $md_seamweld_glinka_layers
        }
    }
	
#update advanced bunching params of the curves attached to the T-connection curves
    
    set ref_glinka_ht 0
    if {[info exists md_seamweld_glinka_height] && $md_seamweld_glinka_height != ""} {
        set ref_glinka_ht $md_seamweld_glinka_height
    }
    if $ref_glinka_ht {
    set ref_glinka_ht_ratio 1
    if {[info exists md_seamweld_glinka_height_ratio] && $md_seamweld_glinka_height_ratio != ""} {
        set ref_glinka_ht_ratio $md_seamweld_glinka_height_ratio
    }
	foreach c $source_curves {
	    set pp [ic_geo_boundary curve $c]
	    if {[lindex $pp 0] != ""} {set ptlist([lindex $pp 0]) 1}
	    if {[lindex $pp 1] != ""} {set ptlist([lindex $pp 1]) 1}
	}
	foreach pt [array names ptlist] {
	    set aclist [ic_geo_incident point $pt]
	    set avg $ref_glinka_ht
	    if !$avg {
		set ctr 0
		set sum 0.
		foreach ac $aclist {
		    if {[lsearch $source_curves $ac] != -1} {
		        catch { unset pp }
		        array set pp [ic_get_meshing_params curve $ac]
		        if {![info exists pp(emax)] || $pp(emax) == ""} continue
		        incr ctr
		        set sum [expr $sum + $pp(emax)]
		    }
		}
		if $ctr {set avg [expr $sum / $ctr]}
            }
            foreach ac $aclist {
                if {[lsearch $source_curves $ac] != -1} continue
		if {[lindex [ic_geo_boundary curve $ac] 0] == $pt} {
		    set start($ac) $avg
		} elseif {[lindex [ic_geo_boundary curve $ac] 1] == $pt} {
		    set end($ac) $avg    
		}
            }
	}
	set scale [ic_geo_get_ref_size]
	if {$scale == ""} { set scale 1.0 }
	foreach c [array names start] {
	    catch { unset bunch }
	    array set bunch [ic_geo_get_curve_bunching $c]
	    if {![info exists bunch(law)] || ($bunch(law) == "geo1" || $bunch(law) == "geo2" || \
	                                      $bunch(law) == "default")} {
	        catch {unset pp}
		array set pp [ic_get_meshing_params curve $c]
		if {![info exists pp(emax)] || $pp(emax) == "" || $pp(emax) == 0} {
		    mess "Can't set Advanced Bunching parameters for curve $c\n"
		    mess "Please specify maximum element size\n" 
		    continue
		}
		set l [ic_geo_crv_length $c]
		set n [expr round($l / ($pp(emax) * $scale))]
		incr n
		if {$n <= 1} {
                    set ask [tk_dialog .curve_bunching "Advanced Bunching" \
                            "The defined spacing would require number of nodes to be increased, do you want\
                             the software to add nodes to the edge as appropriate ?" "" 1 "Yes" "Continue"]
		    switch $ask {
                        -1 { return "" }
                         0 { set add_nodes 1 }
                         1 { continue }
		    }
		}
		if [info exists add_nodes] {
		    set size [expr ($l / (($md_seamweld_glinka_layers * 2.0) + 1.0)) / $scale]
		    eval ic_set_meshing_params curve $c emax $size
		    unset add_nodes
		    set n [expr int(($md_seamweld_glinka_layers * 2) + 2)]
		}
		if {[info exists bunch(law)] && ($bunch(law) == "geo1" || $bunch(law) == "geo2" || \
	                                         $bunch(law) == "default")} {
                    set h1 [expr ($md_seamweld_glinka_height * 1.0) / $md_seamweld_glinka_layers]
                    set h2 $bunch(h2)
                    set nlay1 $md_seamweld_glinka_layers
                    set nlay2 $bunch(nlay2)
                    set r1 $ref_glinka_ht_ratio
                    set r2 $bunch(r2)
                    set law default
                    set law_name BiGeometric
		} else {
                    set h1 [expr ($md_seamweld_glinka_height * 1.0) / $md_seamweld_glinka_layers]
                    set h2 0
                    set nlay1 $md_seamweld_glinka_layers
                    set nlay2 1
                    set r1 $ref_glinka_ht_ratio
                    set r2 1
                    set law geo1
                    set law_name Geometric1
                }
		ic_geo_set_curve_bunching $c $law n $n h1 $h1 r1 $r1 h2 $h2 r2 $r2 lmax 0 nlay1 $nlay1 nlay2 $nlay2
		mess "Advanced Bunching parameters for curve $c set as\n"
		mess "Bunching Law: $law_name, Spacing 1: $h1, Ratio 1: $r1, Number of layers 1: $nlay1\n"
	    }
	}
	
	foreach c [array names end] {
	    catch { unset bunch }
	    array set bunch [ic_geo_get_curve_bunching $c]
	    if {![info exists bunch(law)] || ($bunch(law) == "geo1" || $bunch(law) == "geo2" || \
	                                      $bunch(law) == "default")} {
		catch {unset pp}
		array set pp [ic_get_meshing_params curve $ac]
		if {![info exists pp(emax)] || $pp(emax) == "" || $pp(emax) == 0} {
		    mess "Can't set Advanced Bunching parameters for curve $c\n"
		    mess "Please specify maximum element size\n" 
		    continue
		}
		set l [ic_geo_crv_length $c]
		set n [expr round($l / ($pp(emax) * $scale))]
		incr n
		if {$n <= 1} {
                    set ask [tk_dialog .curve_bunching "Advanced Bunching" \
                            "The defined spacing would require number of nodes to be increased, do you want\
                             the software to add nodes to the edge as appropriate ?" "" 1 "Yes" "Continue"]
		    switch $ask {
                        -1 { return "" }
                         0 { set add_nodes 1 }
                         1 { continue }
		    }
		}
		if [info exists add_nodes] {
		    set size [expr ($l / (($md_seamweld_glinka_layers * 2.0) + 1.0)) / $scale]
		    eval ic_set_meshing_params curve $c emax $size
		    unset add_nodes
		    set n [expr int(($md_seamweld_glinka_layers * 2) + 2)]
		}
		if {[info exists bunch(law)] && ($bunch(law) == "geo1" || $bunch(law) == "geo2" || \
	                                         $bunch(law) == "default")} {
                    set h1 $bunch(h1)
                    set h2 [expr ($md_seamweld_glinka_height * 1.0) / $md_seamweld_glinka_layers]
                    set nlay1 $bunch(nlay1)
                    set nlay2 $md_seamweld_glinka_layers
                    set r1 $bunch(r1)
                    set r2 $ref_glinka_ht_ratio
                    set law default
                    set law_name BiGeometric
		} else {
                    set h1 0
                    set h2 [expr ($md_seamweld_glinka_height * 1.0) / $md_seamweld_glinka_layers]
                    set nlay1 1
                    set nlay2 $md_seamweld_glinka_layers
                    set r1 1
                    set r2 $ref_glinka_ht_ratio
                    set law geo2
                    set law_name Geometric2
                }
		ic_geo_set_curve_bunching $c $law n $n h1 $h1 r1 $r1 h2 $h2 r2 $r2 lmax 0 nlay1 $nlay1 nlay2 $nlay2
		mess "Advanced Bunching parameters for curve $c set as\n"
		mess "Bunching Law: $law_name, Spacing 2: $h2, Ratio 2: $r2, Number of layers 2: $nlay2\n"
	    }
	}
    }	
    return $source_curves
}

## This proc should return the list of curves on which the actual directive should be defined.
## Those curves are not necessarily the "source_curves" since we don't yet know whether we'll
## be doing a "surface_extend" (in which case we return the same source_curves) or a 
## "curtain_surface" (in which case return different source_curves).

proc ic_meshdirect_seam_weld_glinka_geo_create { source_curves target_family extra_args } {
    #
    if ![ic_geo_is_loaded] {
        mess "Error: must have geometry loaded to create seam weld shadow curves\n" red
        return ""
    }
    #
    # --> Try to determine whether or not the glinka-geom has already been created.
    #     If so, return
    #
    set valid_parts [list $target_family [ic_geo_get_part surface [lindex [ic_geo_incident curve [lindex $source_curves 0] 1] 0]]]
    set n_source_curves [llength $source_curves]
    set unglinkaed_source_curves ""
    set connected_unglinkaed_source_curves ""
    set new_source_curves ""
    foreach crv $source_curves {
        # check the family of the curve, see if has seamwelds
        # check num_incident on the curve -- if > 2
        set directive [ic_meshdirect_get_directive_of_fam [ic_geo_get_family curve $crv]]
        set num_incident [llength [ic_geo_incident curve $crv]]
        if {("seamweld" == $directive) && (1 < $num_incident)} {
            md_debug "(1) lappend new_source_curves $crv"
            lappend new_source_curves $crv
        } else {
            if {2 < $num_incident} {
                lappend connected_unglinkaed_source_curves $crv
            } else {
                lappend unglinkaed_source_curves $crv
            }
        }
    }
    if {"" == $unglinkaed_source_curves} {
        #mess "All curves are already attached\n"
        #mess "extra_args are $extra_args\n"
        return [concat $connected_unglinkaed_source_curves $new_source_curves]
    }
    set source_curves $unglinkaed_source_curves
    #
    # --> get the surfaces in the target family
    #    
    set target_surfaces ""
    foreach tf $target_family {
        if [ic_geo_check_family $target_family] {
            eval lappend target_surfaces [ic_geo_get_objects surface $target_family]
        } else {
            mess "Error: could not find target family \"$target_family\" in the geometry\n" red
        }
    }
    if {"" == $target_surfaces} {
        return ""
    }
    #
    # --> If mesh is already loaded, work directly on the mesh
    #
    if [ic_uns_is_loaded] {
        set target_surfs  [ic_geo_get_objects surface $target_family]
        ic_meshdirect_enforce_curve_shadow_nodes_on_surfaces $source_curves $target_surfs
        # ..?but still create geometry? --jka ?fixme? watchme at least
        #return
    }
    #
    # --> Get other args
    #    
    foreach {n v} $extra_args {
        set $n $v
    }
    if {(![info exists md_seamweld_maxprojection]) || ("" == $md_seamweld_maxprojection)} {
        set md_seamweld_maxprojection "-1" 
    }
    if {(![info exists md_seamweld_glinka_layers]) || ("" == $md_seamweld_glinka_layers)} {
        set md_seamweld_glinka_layers "2"
    }
    if {(![info exists md_seamweld_extend_type]) || ("" == $md_seamweld_extend_type)} {
        set md_seamweld_extend_type "1"
    }
    #
    # --> Get the params off of the source curves to set on any new curves
    #     This is overly simple & homogenized, but probably good enough
    #
    foreach sc $source_curves {	
        set params [ic_get_meshing_params curve $sc]
        foreach {n v} $params {
            set tmp_transfer_param($n) $v
        }
    }
    #
    # --> Foreach curves, figure out where it's going, that is, to which surface    
    # 
    #This needs to be improved.  It is doing more work than it needs to. -->BEN
    set curtain_it 0
    set valid_region 0
    foreach crv $source_curves {
        if {"" == [ic_geo_check_objects_exist curve $crv]  } continue     
        foreach srf $target_surfaces {
            if {"" == [ic_geo_check_objects_exist surface $srf]  } continue  
            #
            # --> 
            #
            if {0 < $md_seamweld_maxprojection} {
                set crv_rep_coords [lindex [ic_geo_get_text_point curve $crv] 0]
                set proj_coords [ic_geo_project_point surface $srf $crv_rep_coords {0 0 0}]
                set this_dist [ic_geo_pnt_dist $proj_coords $crv_rep_coords]
                md_debug "md_seamweld_maxprojection = $md_seamweld_maxprojection"
                md_debug "this_dist = $this_dist"
                #
                #   For curved curves, we need to check the distance at more points,
                #   not only at a single text point, see bug 10826.
                #
                if {$this_dist > $md_seamweld_maxprojection} {
                    continue
                }
            }
            #
            # --> 
            #
            if [catch {ic_geo_crv_parent_srf_is_coplanar_to_nearest $crv $srf} c_check] {
                mess "Error in ic_geo_crv_parent_srf_is_coplanar_to_nearest $crv $srf ; giving $c_check\n" red
                set c_check 0
            }
            if $c_check {
                md_debug "coplanar_target_surface = $srf"
                set curtain_it 1
                set valid_region 1
            } else {
                md_debug "nonplanar_target_surface = $srf"
                set valid_region 1
            }
        }
    }
    #
    # --> Track new curves
    #
    set new_side_curves   ""
    set new_srfs          ""
#    set shared_crvs ""
    set stat        1
    set res_srfs    ""
#
#-----  New Approach -> Either extend all source curves, or curtain all source curves
#
    if {$curtain_it && $valid_region } {
        set fam [ic_geo_get_family curve [lindex $source_curves 0]]
        md_debug "curtain family = \"$fam\""
        set build_topo 1
        set res_srfs ""
        foreach curve $source_curves {
            if {"" == [ic_geo_check_objects_exist curve $curve]  } continue 
            if [catch {ic_geo_create_surface_curtain $curve $target_surfaces $curve $fam $build_topo 1} res_srf] {
                mess "Warning: could not run ic_geo_create_surface_curtain $curve $target_surfaces $curve $fam $build_topo 1 \n" red
                set stat 1
            } else {
                set stat 0
                set res_srfs [concat $res_srfs $res_srf]
            }
        }
        md_debug "surface curtain \"$res_srfs\" from curve \"$crv\" to surface \"$target_surfaces\""
        set side_crv_source_crv [ic_meshdirect_seam_weld_glinka_find_valid_connections $res_srfs $source_curves $valid_parts]
        set new_side_curves [concat $new_side_curves [lindex $side_crv_source_crv 0]]
        set new_source_curves [concat $new_source_curves [lindex $side_crv_source_crv 1]]
    }
    if {$stat && $valid_region} {
        set res_srfs ""
        foreach curve $source_curves {
            if {"" == [ic_geo_check_objects_exist curve $curve]  } continue   
            set tol [ic_geo_default_topo_tolerance]
            if [catch {ic_geo_surface_extend $curve $target_surfaces $tol 1 $md_seamweld_extend_type 1} res_srf] {
                mess "Warning: could not run ic_geo_surface_extend $curve $target_surfaces $tol 1 $md_seamweld_extend_type 1 \n" red
                set stat 1
            } else {
                set stat 0
                md_debug "res_srf = $res_srf\n"
                #res_srf = 2 is error
                if {($res_srf == 2) || ("" == $res_srf) || ("" == [ic_geo_check_objects_exist surface $res_srf])} { continue }
                set res_srfs [concat $res_srfs $res_srf]
            }
        }
        md_debug "surface extend \"$res_srfs\" from curves \"$source_curves\" to surface \"$target_surfaces\""
        set side_crv_source_crv \
            [ic_meshdirect_seam_weld_glinka_find_valid_connections $res_srfs $source_curves $valid_parts]
        set new_side_curves [concat $new_side_curves [lindex $side_crv_source_crv 0]]
        set new_source_curves [concat $new_source_curves [lindex $side_crv_source_crv 1]]
    }
    #
    # --> Transfer source_curve params to new_source_curves
    #
    set param_args ""
    foreach p  [array names tmp_transfer_param] {
        append param_args "$p $tmp_transfer_param($p) "
    }
    foreach sc $new_source_curves {
        if {"" == [ic_geo_check_objects_exist curve $sc]} { continue }
        eval ic_set_meshing_params curve $sc $param_args
    }
    #
    # --> Add params to the crv for the proper offset of the glinka layers
    #
    foreach tc $new_source_curves {
        if {"" == [ic_geo_check_objects_exist curve $tc]} { continue }
        if [info exists md_seamweld_glinka_height] {
            ic_set_meshing_params curve $tc ehgt $md_seamweld_glinka_height
        }
        if [info exists md_seamweld_glinka_height_ratio] {
            ic_set_meshing_params curve $tc erat $md_seamweld_glinka_height_ratio
        }
        if [info exists md_seamweld_glinka_layers] {
            ic_set_meshing_params curve $tc ewid [expr $md_seamweld_glinka_layers + 1]
        }
    }
#    mess "new_side_curves are $new_side_curves\n" purple
#    mess "param_args are $param_args\n"
    foreach sc $new_side_curves {
        if {"" == [ic_geo_check_objects_exist curve $sc]} { continue }
        set olparams [ic_get_meshing_params curve $sc]
        foreach {ol wid} $olparams {
            if {$ol == "ewid"} {
                set sidewidth $wid
            }
        }
        if {$sidewidth > 0} {continue}
        eval ic_set_meshing_params curve $sc $param_args
    }
    array unset tmp_transfer_param
    #
    # --> THIS CMD IS A HACK FOR PROBLEMS IN AKO & AWM'S CODE -- FIXME -- REMOVE WHEN POSSIBLE --JKA
    #
    md_debug "ic_meshdirect_seam_weld_glinka_geo_create: ic_build_topo with tolerance [ic_geo_default_topo_tolerance]"
    ic_build_topo [ic_geo_default_topo_tolerance] -quiet
    #
    # --> If a mesh is loaded and surface_extend created a new_surface,
    #     we need to remesh this new surface --jka
    #
    if {[ic_uns_is_loaded] && (0 < [llength $new_srfs])} {
        # watch these params...they are the default from ai_env;  fixme --jka
        md_debug "ic_quad_from_geometry"
        ic_quad_from_geometry surfaces $new_srfs 2 1 20 0.025 0 0 0 1 0.2 1 0 0 0
    }
    md_debug "new_source_curves = $new_source_curves"
    return $new_source_curves
}

proc ic_meshdirect_seam_weld_glinka_find_valid_connections {res_srfs source_curves valid_parts} {
    set new_side_curves ""
    set new_source_curves ""
#mess "res_srfs is $res_srfs\n"
    foreach res_srf $res_srfs {
        if {("" == $res_srf) || ("" == [ic_geo_check_objects_exist surface $res_srf])} { 
            mess "invalid surface $res_srf being passed \
                to ic_meshdirect_seam_weld_glinka_find_valid_connections\n" red
            continue 
        }
        if [catch { ic_geo_boundary surface $res_srf 0 0 1} curtain_bnd_crvs] {
            mess "Error: could not get boundary of surface $res_srf" red
            set stat 1
        } else {
            md_debug "curtain_bnd_crvs = \"$curtain_bnd_crvs\""
            foreach tc $curtain_bnd_crvs {
                #
                # track the *NEW* source curves, those that 
                # shared an edge with the target surface
                #
                # In other words are connected to surfaces of two 
                # parts, and connected to three surfaces (one is most likely dumplicated).
                #
                # we also need to track the *NEW* side-surves
                #
                set check_surfs [ic_geo_incident curve $tc 1]
                md_debug "check_surfs = $check_surfs incident on curve $tc\n"
                if { [llength $check_surfs] < 3} {
                    if {0 > [lsearch $source_curves $tc]} {
                        lappend new_side_curves $tc
                    }
                    continue
                }
                set adj_parts ""
                foreach s $check_surfs {
                    set p [ic_geo_get_part surface $s]
                    if {[lsearch $valid_parts $p] == -1} continue
                    lappend adj_parts $p
                }
                set adj_parts [lsort -unique $adj_parts]
                md_debug "(2) adj_parts = $adj_parts\n"
                if {[llength $adj_parts] > 1} {
                    md_debug "(2) lappend new_source_curves $tc"
                    lappend new_source_curves $tc
                } elseif {0 > [lsearch $source_curves $tc]} {
                    lappend new_side_curves $tc
                }
                md_debug "new_side_curves = \"$new_side_curves\""
            }
        }
    }
    set new_side_curves [lsort -unique $new_side_curves]
    set new_source_curves [lsort -unique $new_source_curves]
#mess "\n\n new_source_curves are $new_source_curves ; \n for new_surfs $res_srfs with valid parts $valid_parts\n" red
    return [list $new_side_curves $new_source_curves]
}

## This proc

proc ic_meshdirect_seam_weld_glinka_geo_valid { glinka_source_family } {

#I don't think this check is useful anymore --> BEN
mess "glinka_source_family is $glinka_source_family\n"

    set weld_is_valid 1

    # --> Get all of the source curves in the glinka_family

    set source_curves [ic_geo_objects_in_family curve $glinka_source_family]
    if {"" == $source_curves} {

        set weld_is_valid 0

    } else {

        # --> Check that each of the curves is attached to 3 surfaces
        #   (v. Bug 8379, comment 6) --jka

        set valid_source_curves 0
        foreach nsc $source_curves {
            if {3 > [llength [ic_geo_incident curve $nsc]]} {
                mess "Warning: could not find enough surfaces attached to the Glinka \
                    curve \"$nsc\" ... please repair\n" red
            } else {
                incr valid_source_curves
            }
        }

        # --> If not valid, make the weld "inactive" and give warning.

        if {[llength $source_curves] > $valid_source_curves} {
            set weld_is_valid 0
        }
    }

    if !$weld_is_valid {
        set glinka_part_name [lindex [split $glinka_source_family ":"] end] ;
        # fixme:naming_convention_assumption --jka
        mess "The Glinka welder was not able to properly connect the \
            surfaces for \"$glinka_part_name\".\
            Making weld inactive.  Please fix the problem by segmenting the surfaces \
            manually and turn Connector back to active once fixed.\nOK\n" red
        ic_meshdirect_directive_set_val_by_tag seamweld $glinka_source_family active 0
    }

    return $weld_is_valid
}


##

proc notyetused_ic_meshdirect_ensure_good_curtain_connectivity { weld_families } {
    #    
    # --> create a map of the weld elements
    #
    set weld_map [ic_uns_subset_create_family_type $weld_families {QUAD_4 TRI_3}]
    if {"" == $weld_map} {
	return ""
    }
    #
    # --> get the single edged elements among them
    #
    set diag_map [ic_uns_create_diagnostic_subset]
    ic_uns_create_diagnostic_edgelist 1
    set stat [ic_uns_diagnostic subset $weld_map diag_type single fix_fam FIX_SINGLE disp_subset $diag_map diag_verb {Single edges} fams {}]
    ic_uns_create_diagnostic_edgelist 0    
    #
    # --> If there aren't any, we're done.
    #
    fixme
    #
    # --> Get all of the non-weld_fam elements attached to the single-edged weld-els
    #
    set remesh_map [ic_uns_get_elements_attached_to_elements $diag_map] ;# maybe des_* args    
    #
    # --> Remesh those
    #
    
}


##

proc fixme {} {
    #
    # --> if there is already a mesh loaded...
    #
    if {0 && [ic_uns_is_loaded]} {
	
	fixme
	
	# --> get the mesh on the source curve
	
	set dim 1
	set source_bars_map [fixme $target_family $dim]
	
	# --> get the extrude vals for the source curve
	
	fixme
	
	# --> extrude the mesh on the source curve
	
	set n_extrude_layers $md_seamweld_glinka_layers
	set extrude_dir {0 0 -1}
	set extrude_spacing 1
	
	set res [ic_extrude map $source_bars_map numlayers $n_extrude_layers \
		     dir $extrude_dir space $extrude_spacing \
		     volf $fam sidef $fam topf $fam]
	# rpoint {0.000000 0.000000 0.000000} rdir {1 0 0} rangle 10.0
	# curve "" curvedir 0 del_orig 0
	
	# --> get the map of n_layers around the source curve
	
	set dim 2
	set target_shells_map [fixme $target_family $dim]
	
	# --> remesh there with offset, prescribing the source_curve (need stuff from ako)
	
	set stat [ic_quad_offset_curve_layers curve_family $source_family $md_seamweld_glinka_layers]
    }
}

#-----------------------

## need doc.

proc ic_meshdirect_setlist {directive list} {
    foreach entry $list {
        ic_meshdirect_set $directive $entry
    }
    return 1
}

## need doc.

proc ic_meshdirect_set {directive flexentry} {
    set curtags [ic_meshdirect_directive_tags_get $directive]
    set properl [expr [llength $curtags] + 2]
    if { [llength [lindex $flexentry 0]] > 1 } {
        return [ic_meshdirect_setlist $directive $flexentry]
    }
    if {0 && ($properl != [llength $flexentry])} {
        mess "ic_meshdirect_set: unrecognized format for $directive\n$flexentry"
        mess "DEBUG: properl: $properl; len: [llength $flexentry]"
        return 1
    }
    # Set the parameters; 0th is the family
    set fam [lindex $flexentry 0]
    # end-1; flexlist_delete is the last element
    ic_meshdirect_directive_set_vals_by_tag $directive $fam [lrange $flexentry 1 end-1]
    #xx make smarter return... --jka
    return 1
}

proc ic_meshdirect_get {directive {fams ""}} {
    if {"" == $fams} { set fams [ic_geo_list_families] }
    set curtags [ic_meshdirect_directive_tags_get $directive]
    md_debug "curtags: $curtags"
    set list ""
    foreach f $fams {
        md_debug "  fam: $f"
        set entry $f
        set ngot 0
        set ntags 0
        if { ($f == "ORFN") || ($f == "VORFN") } continue
        foreach t $curtags {
            md_debug "    tag: $t"
            incr ntags
            set ent [ic_meshdirect_directive_get_val_by_tag $directive $f $t]
            md_debug "      ent: $ent"
            if {"" == $ent} continue
            incr ngot
            append entry " $ent"
        }
        if $ntags {
            if {$ntags == $ngot} {
                lappend list $entry
            } elseif $ngot {
                mess "ntags($ntags) != ngot($ngot): entry $entry\n" red
            }
        }
    }
    return $list
}

## this resets all of the mesh_direct params in the tetin file

proc ic_meshdirect_reset {{directives ""} {fams ""}} {
    md_debug "ic_meshdirect_reset"
    if { $directives == "" } {
        set directives [ic_meshdirect_available_directives_get]
    }
    if { $fams == "" } {
        set fams [ic_geo_list_families]
    }
    foreach d $directives {
        foreach t [ic_meshdirect_directive_tags_get $d] {
            lappend paramlist [ic_meshdirect_sub_param_for_tag $d $t]
        }
        # gotta get all the target_fam params since there are any number
        set ind 1
        set target_fam_base [ic_meshdirect_sub_param_for_tag $d target_fam]
        set target_fam_cur_tag $target_fam_base
        # this "20" is horrible, but...fixme --jka
        while {$ind < 20} {
            lappend paramlist $target_fam_cur_tag
            set target_fam_cur_tag "${target_fam_base}${ind}"
            incr ind
        }
        if [info exists paramlist] { ic_geo_reset_family_params $fams $paramlist }
        #
        # delete the icon for all given $fams for this directive $d
        #
        ic_meshdirect_delete_icon $d $fams
    }
}


## returns the list of params from the tetin file, in the style of the flexlist

proc ic_meshdirect_get_current_for_flexlist {{directives ""} {fams ""}} {
    if { $directives == "" } {
        set directives [ic_meshdirect_available_directives_get]
    }
    #if { $fams == "" } { set fams [ic_geo_list_families] }
    # this is done for us in ic_meshdirect_get --jka
    set list ""
    foreach d $directives {
        # uhh, well.... xxx --jka
        if ![info exists meshdirect_vals(${d}_active)] {
            set meshdirect_vals(${d}_active) 1
        }
        if $meshdirect_vals(${d}_active) {
            if { $fams != "" } {
                foreach f $fams {
                    if { ($f == "ORFN") || ($f == "VORFN") } continue
                    md_debug "d: $d; fam: $f"
                    set val [ic_meshdirect_get $d $f]
                    md_debug "val: $val"
                    if {"" == $val} { continue }
                    lappend list $val
                }
            } else {
                if {![llength $list]} {
                    lappend list [ic_meshdirect_get $d "" ]
                } else {
                    set list [ic_meshdirect_get $d ""]
                }
            }
        }
    }
    return $list
}


## This procedure finds curves in a part within a distance of other parts.
## distance is distance between parts
## parts is a list of parts to check
## weld_args are the weld arguments that need to be passed to ic_meshdirect_seamweld_set

proc ic_meshdirect_find_curve_contacts {distance {parts ""} {weld_args ""} } {
    if {$parts == ""} { set parts [ic_geo_list_parts] }
    if ![llength $parts] return
    # create arrays of single_edges per part, neighbors of parts and the
    #   single_edge_points in part and their neighbors
    foreach p $parts {
        set single_edges($p) [scrib_lib_geo_get_single_curves [ic_geo_get_objects curve $p]]
        set single_edge_points($p) [ic_geo_get_points_from_curves $single_edges($p)]
        set part_neighbor($p) ""
        foreach sp $single_edge_points($p) {
            set neighbors($sp) ""
            foreach pp $parts {
                if {$pp == $p} continue
                set dist [scrib_lib_geo_distance_from_surfaces $pp [ic_geo_get_point_location $sp]]
                if {$dist > $distance} continue
                lappend neighbors($sp) $pp
                lappend part_neighbor($p) $pp
                md_debug "neighbors($sp) = $neighbors($sp)"
            }
        }
        set part_neighbor($p) [lsort -unique $part_neighbor($p) ]
        md_debug "part_neighbor($p) = $part_neighbor($p)"
    }
    md_debug ""
    # foreach single curve in each part find if it can be welded to any neighbor parts
    # this sets up an array base_part_curves that is a list of curves that weld to the part
    set weld_regions ""
    foreach p $parts {
        md_debug "p = $p"
        foreach se $single_edges($p) {
            md_debug "se = $se"
            set points [ic_geo_boundary curve $se]
            md_debug "points = $points"
            if {$points == ""} {continue}
            if {[llength $points] < 2} {continue}
            set parts_to_weld_to [concat $neighbors([lindex $points 0]) $neighbors([lindex $points 1])]
            # For first cut, only parts with both points projecting to same part should get weld defined.
            set parts_to_weld_to [lsort -unique [scrib_lib_utils_list_get_duplicate $parts_to_weld_to]]
            if {[llength $parts_to_weld_to] == 0} {continue}
            #mess "curve $se has parts to weld to $parts_to_weld_to \n points are $points \n"
            set current_part [ic_geo_get_part curve $se]
            set seam_crv_fam $current_part\:SEAM_CURVE
            set st1 [expr [string last / $current_part] +1]
            set st2 [expr [string length $current_part] - 8]
            if {$st1 > 1} {set st $st1} else {set st $st2}
            append seam_crv_fam _[string range $current_part $st end]
            foreach pp $parts_to_weld_to {
                set st1 [expr [string last / $pp] + 1]
                set st2 [expr [string length $pp] - 8]
                if {$st1 > 1} {set st $st1} else {set st $st2}
                append seam_crv_fam _[string range $pp $st end]
                lappend base_part_curves($pp) $se
            }
            md_debug "new curve fam is $seam_crv_fam\n"
            if { [string length $seam_crv_fam] > 100 } {
                set seam_crv_fam [string range $seam_crv_fam 0 97]
            }
            md_debug "new curve fam after truncation $seam_crv_fam\n"
            ic_geo_set_family curve $seam_crv_fam names $se 0
            lappend weld_regions $seam_crv_fam
            lappend weld_parts($seam_crv_fam) $parts_to_weld_to
        }
    }
    set weld_regions [lsort -unique $weld_regions]
    if {$weld_args == ""} {
        set weld_args [list md_seamweld_method curtain_quad md_seamweld_active 1 \
            md_seamweld_splitting remesh_tri_quad md_seamweld_maxprojection $distance ]
    }
    foreach {warg wp} $weld_args {
        if {$warg == "md_seamweld_method"} {
            set wtyp $wp
        }
    }
    #mess "weld_args are $weld_args\n"
    #now define the connectors
    foreach w $weld_regions {
        if ![info exists weld_parts($w)] continue
        set weld_parts($w) [lsort -unique $weld_parts($w)]
        set regions [scrib_lib_geo_make_conn_regions curve [ic_geo_get_objects curve $w]]
        #mess "regions are $regions\n"
        if {$wtyp == "standard" || $wtyp == "glinka"} {
            set resName [aie_connectors_default_result_group_name "seamweld"]
            set resPart [aie_connectors_register_new_result_group $resName]
        }
        if {[llength $regions] == 1} {
            foreach pp $weld_parts($w) {
                switch $wtyp {
                "standard" {
                    ic_meshdirect_seamweld_set $w $pp $resPart $weld_args
                }
                "curtain_quad" {
                    ic_meshdirect_seamweld_set $w $pp $w $weld_args
                }
                "glinka" {
                    set crvs [ic_geo_get_objects curve $w]
                    if {$crvs == "" } continue
                    set orig_part [ic_geo_get_part curve [lindex $crvs 0]]
                    ic_geo_set_family curve $orig_part names $crvs 0
                    set x [ic_meshdirect_seam_weld_glinka_geo_create $crvs $pp $weld_args]
                    ic_geo_set_family curve $w names $x 0
                    ic_meshdirect_seamweld_set $w $pp $resPart $weld_args
                }
                }
            }
        } else {
            # This operates on glinka regions that are disconnected but going to same part
            set ii 1
            foreach reg $regions {
                set new_part $w\_$ii
                incr ii
                set orig_part [ic_geo_get_part curve [lindex $reg 0]]
                ic_geo_set_family curve $new_part names $reg 0
                foreach pp $weld_parts($w) {
                    switch $wtyp {
                        "standard" {
                            ic_meshdirect_seamweld_set $new_part $pp $resPart $weld_args
                        }
                        "curtain_quad" {
                            ic_meshdirect_seamweld_set $new_part $pp $new_part $weld_args
                        }
                        "glinka" {
                            set crvs $reg
                            if {$crvs == "" } continue
                            #mess "Running ic_meshdirect_seam_weld_glinka_geo_create $crvs $pp $weld_args \n" blue
                            #mess "weld_parts($w) is $weld_parts($w) ; w is $w \n"
                            #mess "regions are $regions ; reg is $reg\n"
                            set x [ic_meshdirect_seam_weld_glinka_geo_create $crvs $pp $weld_args]
                            ic_geo_set_family curve $new_part names $x 0
                            ic_meshdirect_seamweld_set $new_part $pp $new_part $weld_args
                        }
                    }
                }
            }
        }
    }
}

proc ic_meshdirect_seam_weld_glinka_thickness_attached_to_curve { crv {warn 0}} {
    if $warn {mess "calculating minimum surface thickness at curve $crv\n"}
    set min 0
    set ss [ic_geo_incident curve $crv]
    foreach s $ss {
        catch {unset pp}
        array set pp [ic_get_meshing_params surface $s]
        set thk 0
        if {[info exists pp(ehgt)] && $pp(ehgt)!=""} {
            set thk $pp(ehgt)
        }
        if !$thk {
            if $warn {mess "zero surface thickness for $s\n"}
        } else {
            if !$min {set min $thk}
            if {$thk<$min} {set min $thk}
        }
    }
    if $warn {mess "returning $min\n"}
    return $min
}
