edge
TCL/TK UseCase3
Abstract : Contains all procedure for creating and maintaining an edge object.
Comments : The procedures in this file for getting and setting can easily be 
           converted to call propagation patterns.
#
#edge.tcl
#
#Contains procedures for creating new edges and for retrieving and setting
#data related to edge objects
#
###
# Bend node ovels...
###
###
#Calculates how to size the bendy node ovals
###
proc calcOval { x y } {
    set width 6
    set left [expr $x - ($width / 2)]
    set top [expr $y - ($width / 2)]
    set right [expr $left + $width]
    set bottom [expr $top + $width]
    return "$left $top $right $bottom"
}
###
#Determines which bendy node of an edge is selected
###
proc findListEntry { theList itemID } {
    set index 0
    foreach i $theList {
	if {[lindex $i 0] == $itemID} {
	    return $index
	}
	incr index
    }
    return -1
}
###
# Set up an edge "data structure."
###
proc newEdge { name type srcVert dstVert {lblOff {0 -20}} {nodes {}}} {
#Makes global vars accessible
    global Edge iGraph inform changes
#Initializes vars in Edge array
    set Edge($name,Label) $name
    set Edge($name,LabelOff) $lblOff
    set Edge($name,srcVert) $srcVert
    set Edge($name,dstVert) $dstVert
    set Edge($name,nodeCoords) $nodes
    set Edge($name,Type) $type
#Set pointers to procedures on how to creates the edge and edge label.
#There are different was for creating different edges.
    set Edge($name,cmdCreate) createEdge_$type
    set Edge($name,cmdCreateLbl) createEdgeLabel_$type
# initialize the item IDs (used for Tcl/Tk binding.)
    set Edge($name,LabelID) -1
    set Edge($name,LineIDs) {}
    set Edge($name,OvalIDs) {}
# reformats some data that will be passed to C++
    puts $nodes
    if {$nodes != ""} {
        set formatted "{$nodes}"
    } else {
        set formatted ""
    }
    set formatted2 "{$lblOff}"
   
#Makes C++ calls for new edge. Only does this for user interaction.
#Also changes global flag "changes" to yes for a save changes check.
  if {$inform == 1} {
    if {$type == "Constr"} {
       set changes yes
	puts "Sending: $name $srcVert $dstVert $formatted2 $formatted"
	$iGraph cppCreCEdge: $name $srcVert $dstVert $formatted2
# $formatted
    } else {
       set changes yes
	puts "Sending: $srcVert $dstVert $formatted2 $formatted"
	$iGraph cppCreAEdge: $srcVert $dstVert
# $formatted
    }
    }
#Adds new edge to the edge lists of its source and destination
    appendVertexEdgeOut $srcVert $name
    appendVertexEdgeIn $dstVert $name
}
###
# The following functions are used to set and obtain data
# relating to edge objects.  If the need be, these functions
# could be used to call propogation patterns that would
# set or return the item requested.
###
#
#Returns the type of a given edge.
proc getEdgeType { name } {
    global Edge
    return $Edge($name,Type)
}
#
#Sets the label of a given edge
proc setEdgeLabel { name lbl } {
    global Edge
    set Edge($name,Label) $lbl
}
#
#Returns the label of an edge
proc getEdgeLabel { name } {
    global Edge
    return $Edge($name,Label)
}
#
# Sends label update to C++
proc setEdgeLabelOff { name offset } {
    global Edge iGraph changes
       set changes yes
    set Edge($name,LabelOff) $offset
    set formatted "{$offset}"
#    puts "Sending: $name [getEdgeSrcVert $name] [getEdgeDstVert $name]\
#	$formatted"
    $iGraph cppUpdELabel: $name [getEdgeSrcVert $name] [getEdgeDstVert $name]\
	$formatted
}
#
#Returns the label offset for an edge
proc getEdgeLabelOff { name } {
    global Edge
    return $Edge($name,LabelOff)
}
#
#Returns the comand to create an edge.
proc getEdgeCreateCmd { name } {
    global Edge
    return $Edge($name,cmdCreate)
}
#
#Returns the command to create an edge label
proc getEdgeCreateLblCmd { name } {
    global Edge
    return $Edge($name,cmdCreateLbl)
}
#
#Sets the source vertex of an edge
proc setEdgeSrcVert { name vert } {
    global Edge
    set Edge($name,srcVert) $vert
}
#
#Returns the source vertex of an edge
proc getEdgeSrcVert { name } {
    global Edge
    return $Edge($name,srcVert)
}
#
#Sets the destination vertex of an edge
proc setEdgeDstVert { name vert } {
    global Edge
    set Edge($name,dstVert) $vert
}
#
#Returns the destination vertex of an edge
proc getEdgeDstVert { name } {
    global Edge
    return $Edge($name,dstVert)
}
#
# Informs C++ of start/end node for an edge.
proc setEdgeNodeCoords { name coords } {
    global Edge iGraph changes
       set changes yes
    set Edge($name,nodeCoords) $coords
    set formatted "{$coords}"
    if {[getEdgeType $name] == "Constr"} {
#	puts "Sending: $name [getEdgeSrcVert $name] \
	        [getEdgeDstVert $name] $formatted"
	$iGraph cppUpdCEdge: $name [getEdgeSrcVert $name] \
		[getEdgeDstVert $name] $formatted
    } else {
#	puts "Sending: [getEdgeSrcVert $name] [getEdgeDstVert $name]\
#              $formatted"
	$iGraph cppUpdAEdge: [getEdgeSrcVert $name] [getEdgeDstVert $name] $formatted
    }
}
#
#Returns the node coordinates for a bendy node
proc getEdgeNodeCoords { name } {
    global Edge
    return $Edge($name,nodeCoords)
}
#
#Sets the label id for an edge
proc setEdgeLabelID { name id } {
    global Edge
    set Edge($name,LabelID) $id
}
#
#Returns the label id for an edge
proc getEdgeLabelID { name } {
    global Edge
    return $Edge($name,LabelID)
}
#
#Sets the line id for an edge
proc setEdgeLineIDs { name ids } {
    global Edge
    set Edge($name,LineIDs) $ids
}
#
#Returns the line id for an edge
proc getEdgeLineIDs { name } {
    global Edge
    return $Edge($name,LineIDs)
}
#
#Sets the oval id for an edge
proc setEdgeOvalIDs { name ids } {
    global Edge
    set Edge($name,OvalIDs) $ids
}
#
#Returns the oval id for an edge
proc getEdgeOvalIDs { name } {
    global Edge
    return $Edge($name,OvalIDs)
}
#
#Clears the edge ids for an edge that is being refreshed
proc clearEdgeIDs { name } {
    global Edge
    set Edge($name,LineIDs) {}
    set Edge($name,OvalIDs) {}
}
###
#Deletes an edge associated with a node that has been moved (screen refresh)
###
proc deleteEdgeObjects { name } {
    global Edge can
#Deletes lines
    foreach i $Edge($name,LineIDs) { 
	for {set x 0} {$x < [llength $i]} {incr x 2} {
	    $can delete [lindex $i $x] 
	}
    }
#Deletes bendy point ovals
    foreach i $Edge($name,OvalIDs) { $can delete [lindex $i 0] }
#Deletes the label
    $can delete [getEdgeLabelID $name]
    clearEdgeIDs $name
    setEdgeLabelID $name -1
}
###
#Creates a construction edge on the canvas
###
proc createEdge_Constr { name srcCoords dstCoords lst index {arrow 1} } {
    global can
    upvar $lst List
#Creates the line
    set itemID [$can create line \
	    [lindex $srcCoords 0] [lindex $srcCoords 1] \
	    [lindex $dstCoords 0] [lindex $dstCoords 1] \
	    -tags "$name"]
#Creates the arrow
    if {$arrow != 0} {
	$can itemconfigure $itemID -arrow last
    }
    lappend List "$itemID $index"
    $can raise $itemID
    $can bind $itemID  " onEdgeLineDbl $name %x %y "
    return $itemID
}
###
# Calulate || lines for alternation edges.
###
proc calcAlterSlope { SourceX SourceY DestX DestY } {
    set space 3
    if {[expr ($DestX - $SourceX)] != 0} {
#Determines the slope of the edge
        set slope [expr abs((($DestY - $SourceY) * 100) / ($DestX - $SourceX))]
#For more vertically inclined edges, offsets x coords
        if {[expr $slope] >= 100} {
            set xspace $space
            set yspace 0
        } else {
#For more horizontally inclined edges, offsets y coords
            set xspace 0
            set yspace $space
        }
    } else {
        set yspace 0
        set xspace $space
    }
    return "$xspace $yspace"
}
###
#Creates an alternation edge on the canvas.
###
proc createEdge_Alter { name srcCoords dstCoords lst index {arrow 1} } {
    global can
    upvar $lst List
#Determine slope and offsets for lines
    set slope [calcAlterSlope [getXCoord $srcCoords] [getYCoord $srcCoords] \
	    [getXCoord $dstCoords] [getYCoord $dstCoords]]
    set xspace [getXCoord $slope]
    set yspace [getYCoord $slope]
#Create first line with arrow
    set itemID [$can create line \
	    [expr [lindex $srcCoords 0] - $xspace ] \
	    [expr [lindex $srcCoords 1] - $yspace ] \
	    [expr [lindex $dstCoords 0] - $xspace ] \
	    [expr [lindex $dstCoords 1] - $yspace ] \
	    -tags "$name"]
    if {$arrow != 0} {
	$can itemconfigure $itemID -arrow last
    }
#Create second line with arrow
    set itemID2 [$can create line \
	    [expr [lindex $srcCoords 0] + $xspace ] \
	    [expr [lindex $srcCoords 1] + $yspace ] \
	    [expr [lindex $dstCoords 0] + $xspace ] \
	    [expr [lindex $dstCoords 1] + $yspace ] \
	    -tags "$name"]
    if {$arrow != 0} {
	$can itemconfigure $itemID2 -arrow last
    }
    lappend List "$itemID $index $itemID2 $index"
    $can raise $itemID
    $can raise $itemID2
#Create bindings for removing and adding bends
    $can bind $itemID  " onEdgeLineDbl $name %x %y "
    $can bind $itemID2  " onEdgeLineDbl $name %x %y "
    return "$itemID $itemID2"
}
###
# Find midpoint between 2 nodes.
###
proc edgeCalcMidpoint { name } {
#Initialize vars
    set srcVertName [getEdgeSrcVert $name]
    set dstVertName [getEdgeDstVert $name]
    set srcCoords [getVertexCoords $srcVertName]
    set dstCoords [getVertexCoords $dstVertName]
#Calculate midpoint
    set srcCoords [getVertexCoords $srcVertName]
    set dstCoords [getVertexCoords $dstVertName]
    set midX [expr (([getXCoord $dstCoords] - [getXCoord $srcCoords]) / 2) + \
	    [getXCoord $srcCoords]]
    set midY [expr (([getYCoord $dstCoords] - [getYCoord $srcCoords]) / 2) + \
	    [getYCoord $srcCoords]]
    return "$midX $midY"
}
###
#Create the label for a construction edge.
###
proc createEdgeLabel_Constr { name } {
    global can
#Initialize vars
    set srcVertName [getEdgeSrcVert $name]
    set dstVertName [getEdgeDstVert $name]
    set srcCoords [getVertexCoords $srcVertName]
    set dstCoords [getVertexCoords $dstVertName]
#Calculate midpoint
    set srcCoords [getVertexCoords $srcVertName]
    set dstCoords [getVertexCoords $dstVertName]
    set midX [expr (([getXCoord $dstCoords] - [getXCoord $srcCoords]) / 2) + \
	    [getXCoord $srcCoords]]
    set midY [expr (([getYCoord $dstCoords] - [getYCoord $srcCoords]) / 2) + \
	    [getYCoord $srcCoords]]
#Determine label coordinates based on midpoint of line
    set labelX [expr $midX + [getXCoord [getEdgeLabelOff $name]]]
    set labelY [expr $midY + [getYCoord [getEdgeLabelOff $name]]]
#Create the label object
    set labelName [getEdgeLabel $name]
    set label [$can create text $labelX $labelY \
            -text $labelName -tags "_label_ $name"]
#Set bindings
    $can bind $label  " onEdgeLblMove $name %x %y "
    setEdgeLabelID $name $label
}
#
#No such thing!
proc createEdgeLabel_Alter { name } {
}
###
#Creates an edge on the canvas
###
proc createEdge { name } {
    global can
#Removes any existing edge
    deleteEdgeObjects $name
#Initialize vars
    set createEdgeCmd [getEdgeCreateCmd $name]
    set srcVertName [getEdgeSrcVert $name]
    set dstVertName [getEdgeDstVert $name]
    set lnCoords [getEdgeNodeCoords $name]
    set lnCoords [linsert $lnCoords 0 [getVertexCoords $srcVertName]]
    lappend lnCoords [getVertexCoords $dstVertName]
    set srcCoords [lindex $lnCoords 0]
    
    set Lines {}
    set Ovals {}
#Create bendy nodes
    for {set index 1} { $index < [expr [llength $lnCoords] - 1]} {incr index} {
	#Get destination of next line
	set dstCoords [lindex $lnCoords $index]
	set itemID [$createEdgeCmd $name $srcCoords $dstCoords Lines $index]
	# lappend Lines "$itemID $index"
	#Create oval node
	set ovalCoords [calcOval [lindex $dstCoords 0] [lindex $dstCoords 1]]
	set itemID [$can create oval \
		[lindex $ovalCoords 0] [lindex $ovalCoords 1] \
		[lindex $ovalCoords 2] [lindex $ovalCoords 3] \
		-fill red]
	lappend Ovals "$itemID $index"
	$can raise $itemID
#Create bindings for bend points
	# $can bind $itemID  { onOvalDown %x %y }
	$can bind $itemID  " onOvalMove $name %x %y "
	$can bind $itemID  " onEdgeOvalUp $name %x %y "
	$can bind $itemID  " onEdgeOvalDbl $name %x %y "
	set srcCoords $dstCoords
    }
#Create edge
    set dstCoords [lindex $lnCoords $index]
    set itemID [$createEdgeCmd $name $srcCoords $dstCoords Lines $index 0]
    # lappend Lines "$itemID $index"
#Add arrows to lines
    if {$index == 1} {
	foreach i $itemID {
	    $can itemconfigure $i -arrow last
	}
    }
#Set ids for lines and nodes
    setEdgeLineIDs $name $Lines
    setEdgeOvalIDs $name $Ovals
#Create edge label
    set cmdCreateLabel [getEdgeCreateLblCmd $name]
    $cmdCreateLabel $name
#Add bindings for edges
    $can bind $name  " onEdgeEnter $name %x %y "
    $can bind $name  " onEdgeLeave $name %x %y "
    $can bind $name  " onEdgeUp $name %x %y "
}
###
#Generic loop to search through a list of edge items
###
proc getEdgeItemIndex { theList itemID } {
    set index 0
    foreach i $theList {
	for {set x 0} {$x < [llength $i]} {incr x 2} {
	    if {[lindex $i $x] == $itemID} {
		return $index
	    }
	    incr index
	}
    }
    return -1
}
 
 
 
 
 
 
 
 
