diff options
Diffstat (limited to 'library/graph.tcl')
-rw-r--r-- | library/graph.tcl | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/library/graph.tcl b/library/graph.tcl new file mode 100644 index 0000000..8c4fd8d --- /dev/null +++ b/library/graph.tcl @@ -0,0 +1,842 @@ + +namespace eval ::blt::legend { + variable _private + array set _private { + afterId "" + scroll 0 + space off + drag 0 + x 0 + y 0 + } +} + +namespace eval ::blt::ZoomStack { + variable _private + array set _private { + afterId "" + scroll 0 + space off + drag 0 + x 0 + y 0 + } +} + +option add *zoomOutline.dashes 4 +option add *zoomOutline.lineWidth 2 +option add *zoomOutline.xor yes +option add *zoomTitle.anchor nw +option add *zoomTitle.coords "-Inf Inf" +option add *zoomTitle.font "Arial 14" +option add *zoomTitle.foreground yellow3 +option add *zoomTitle.shadow yellow4 + +# ---------------------------------------------------------------------- +# +# Initialize -- +# +# Invoked by internally by Treeview_Init routine. Initializes +# the default bindings for the treeview widget entries. These +# are local to the widget, so they can't be set through the +# widget's class bind tags. +# +# ---------------------------------------------------------------------- +proc blt::LegendSelections { w } { + if 0 { + # + # Active entry bindings + # + $w legend bind all <Enter> { + %W entry highlight current + } + $w legend bind all <Leave> { + %W entry highlight "" + } + } + + # + # ButtonPress-1 + # + # Performs the following operations: + # + # 1. Clears the previous selection. + # 2. Selects the current entry. + # 3. Sets the focus to this entry. + # 4. Scrolls the entry into view. + # 5. Sets the selection anchor to this entry, just in case + # this is "multiple" mode. + # + + $w legend bind all <ButtonPress-1> { + blt::legend::SetSelectionAnchor %W current + set blt::legend::_private(scroll) 1 + } + + # + # B1-Motion + # + # For "multiple" mode only. Saves the current location of the + # pointer for auto-scrolling. Resets the selection mark. + # + $w legend bind all <B1-Motion> { + set blt::legend::_private(x) %x + set blt::legend::_private(y) %y + set elem [%W legend get @%x,%y] + if { $elem != "" } { + if { [%W legend cget -selectmode] == "multiple" } { + %W legend selection mark $elem + } else { + blt::legend::SetSelectionAnchor %W $elem + } + } + } + + # + # ButtonRelease-1 + # + # For "multiple" mode only. + # + $w legend bind all <ButtonRelease-1> { + if { [%W legend cget -selectmode] == "multiple" } { + %W legend selection anchor current + } + after cancel $blt::legend::_private(afterId) + set blt::legend::_private(scroll) 0 + } + + # + # Shift-ButtonPress-1 + # + # For "multiple" mode only. + # + + $w legend bind all <Shift-ButtonPress-1> { + if { [%W legend cget -selectmode] == "multiple" && + [%W legend selection present] } { + if { [%W legend get anchor] == "" } { + %W legend selection anchor current + } + set elem [%W legend get anchor] + %W legend selection clearall + %W legend selection set $elem current + } else { + blt::legend::SetSelectionAnchor %W current + } + } + $w legend bind all <Shift-Double-ButtonPress-1> { + # do nothing + } + $w legend bind all <Shift-B1-Motion> { + # do nothing + } + $w legend bind all <Shift-ButtonRelease-1> { + after cancel $blt::legend::_private(afterId) + set blt::legend::_private(scroll) 0 + } + + # + # Control-ButtonPress-1 + # + # For "multiple" mode only. + # + $w legend bind all <Control-ButtonPress-1> { + if { [%W legend cget -selectmode] == "multiple" } { + set elem [%W legend get current] + %W legend selection toggle $elem + %W legend selection anchor $elem + } else { + blt::legend::SetSelectionAnchor %W current + } + } + $w legend bind all <Control-Double-ButtonPress-1> { + # do nothing + } + $w legend bind all <Control-B1-Motion> { + # do nothing + } + $w legend bind all <Control-ButtonRelease-1> { + after cancel $blt::legend::_private(afterId) + set blt::legend::_private(scroll) 0 + } + + $w legend bind all <Control-Shift-ButtonPress-1> { + if { [%W legend cget -selectmode] == "multiple" && + [%W legend selection present] } { + if { [%W legend get anchor] == "" } { + %W selection anchor current + } + if { [%W legend selection includes anchor] } { + %W legend selection set anchor current + } else { + %W legend selection clear anchor current + %W legend selection set current + } + } else { + blt::legend::SetSelectionAnchor %W current + } + } + $w legend bind all <Control-Shift-Double-ButtonPress-1> { + # do nothing + } + $w legend bind all <Control-Shift-B1-Motion> { + # do nothing + } + $w legend bind all <KeyPress-Up> { + blt::legend::MoveFocus %W previous.row + if { $blt::legend::_private(space) } { + %W legend selection toggle focus + } + } + $w legend bind all <KeyPress-Down> { + blt::legend::MoveFocus %W next.row + if { $blt::legend::_private(space) } { + %W legend selection toggle focus + } + } + $w legend bind all <KeyPress-Left> { + blt::legend::MoveFocus %W previous.column + if { $blt::legend::_private(space) } { + %W legend selection toggle focus + } + } + $w legend bind all <KeyPress-Right> { + blt::legend::MoveFocus %W next.column + if { $blt::legend::_private(space) } { + %W legend selection toggle focus + } + } + $w legend bind all <KeyPress-space> { + if { [%W legend cget -selectmode] == "single" } { + if { [%W legend selection includes focus] } { + %W legend selection clearall + } else { + %W legend selection clearall + %W legend selection set focus + } + } else { + %W legend selection toggle focus + } + set blt::legend::_private(space) on + } + + $w legend bind all <KeyRelease-space> { + set blt::legend::_private(space) off + } + $w legend bind all <KeyPress-Return> { + blt::legend::MoveFocus %W focus + set blt::legend::_private(space) on + } + $w legend bind all <KeyRelease-Return> { + set blt::legend::_private(space) off + } + $w legend bind all <KeyPress-Home> { + blt::legend::MoveFocus %W first + } + $w legend bind all <KeyPress-End> { + blt::tv::MoveFocus %W last + } +} + +proc blt::legend::SetSelectionAnchor { w tagOrId } { + set elem [$w legend get $tagOrId] + # If the anchor hasn't changed, don't do anything + if { $elem != [$w legend get anchor] } { + $w legend selection clearall + $w legend focus $elem + $w legend selection set $elem + $w legend selection anchor $elem + } +} + +# ---------------------------------------------------------------------- +# +# MoveFocus -- +# +# Invoked by KeyPress bindings. Moves the active selection to +# the entry <where>, which is an index such as "up", "down", +# "prevsibling", "nextsibling", etc. +# +# ---------------------------------------------------------------------- +proc blt::legend::MoveFocus { w elem } { + catch {$w legend focus $elem} result + puts stderr "result=$result elem=$elem" + if { [$w legend cget -selectmode] == "single" } { + $w legend selection clearall + $w legend selection set focus + $w legend selection anchor focus + } +} + + +proc Blt_ActiveLegend { g } { + $g legend bind all <Enter> [list blt::ActivateLegend $g ] + $g legend bind all <Leave> [list blt::DeactivateLegend $g] + $g legend bind all <ButtonPress-1> [list blt::HighlightLegend $g] +} + +proc Blt_Crosshairs { g } { + blt::Crosshairs $g +} + +proc Blt_ResetCrosshairs { g state } { + blt::Crosshairs $g "Any-Motion" $state +} + +proc Blt_ZoomStack { g args } { + array set params { + -mode click + } + array set params $args + if { $params(-mode) == "click" } { + blt::ZoomStack::ClickClick $g + } else { + blt::ZoomStack::ClickRelease $g + } +} + +proc Blt_PrintKey { g } { + blt::PrintKey $g +} + +proc Blt_ClosestPoint { g } { + blt::ClosestPoint $g +} + +# +# The following procedures that reside in the "blt" namespace are +# supposed to be private. +# + +proc blt::ActivateLegend { g } { + set elem [$g legend get current] + $g legend activate $elem +} +proc blt::DeactivateLegend { g } { + set elem [$g legend get current] + $g legend deactivate $elem +} + +proc blt::HighlightLegend { g } { + set elem [$g legend get current] + if { $elem != "" } { + set relief [$g element cget $elem -legendrelief] + if { $relief == "flat" } { + $g element configure $elem -legendrelief raised + $g element activate $elem + } else { + $g element configure $elem -legendrelief flat + $g element deactivate $elem + } + } +} + +proc blt::Crosshairs { g {event "Any-Motion"} {state "on"}} { + $g crosshairs $state + bind crosshairs-$g <$event> { + %W crosshairs configure -position @%x,%y + } + bind crosshairs-$g <Leave> { + %W crosshairs off + } + bind crosshairs-$g <Enter> { + %W crosshairs on + } + $g crosshairs configure -color red + if { $state == "on" } { + blt::AddBindTag $g crosshairs-$g + } elseif { $state == "off" } { + blt::RemoveBindTag $g crosshairs-$g + } +} + +proc blt::PrintKey { g {event "Shift-ButtonRelease-3"} } { + bind print-$g <$event> { Blt_PostScriptDialog %W } + blt::AddBindTag $g print-$g +} + +proc blt::ClosestPoint { g {event "Control-ButtonPress-2"} } { + bind closest-point-$g <$event> { + blt::FindElement %W %x %y + } + blt::AddBindTag $g closest-point-$g +} + +proc blt::AddBindTag { widget tag } { + set oldTagList [bindtags $widget] + if { [lsearch $oldTagList $tag] < 0 } { + bindtags $widget [linsert $oldTagList 0 $tag] + } +} + +proc blt::RemoveBindTag { widget tag } { + set oldTagList [bindtags $widget] + set index [lsearch $oldTagList $tag] + if { $index >= 0 } { + bindtags $widget [lreplace $oldTagList $index $index] + } +} + +proc blt::FindElement { g x y } { + array set info [$g element closest $x $y -interpolate yes] + if { ![info exists info(name)] } { + beep + return + } + # -------------------------------------------------------------- + # find(name) - element Id + # find(index) - index of closest point + # find(x) find(y) - coordinates of closest point + # or closest point on line segment. + # find(dist) - distance from sample coordinate + # -------------------------------------------------------------- + set markerName "bltClosest_$info(name)" + catch { $g marker delete $markerName } + $g marker create text -coords { $info(x) $info(y) } \ + -name $markerName \ + -text "$info(name): $info(dist)\nindex $info(index)" \ + -font "Arial 6" \ + -anchor center -justify left \ + -yoffset 0 -bg {} + + set coords [$g invtransform $x $y] + set nx [lindex $coords 0] + set ny [lindex $coords 1] + + $g marker create line -coords "$nx $ny $info(x) $info(y)" \ + -name line.$markerName + + blt::FlashPoint $g $info(name) $info(index) 10 + blt::FlashPoint $g $info(name) [expr $info(index) + 1] 10 +} + +proc blt::FlashPoint { g name index count } { + if { $count & 1 } { + $g element deactivate $name + } else { + $g element activate $name $index + } + incr count -1 + if { $count > 0 } { + after 200 blt::FlashPoint $g $name $index $count + update + } else { + eval $g marker delete [$g marker names "bltClosest_*"] + } +} + + +proc blt::ZoomStack::Init { g } { + variable _private + set _private($g,interval) 100 + set _private($g,afterId) 0 + set _private($g,A,x) {} + set _private($g,A,y) {} + set _private($g,B,x) {} + set _private($g,B,y) {} + set _private($g,stack) {} + set _private($g,corner) A +} + +proc blt::ZoomStack::ClickClick { g {start "ButtonPress-1"} {reset "ButtonPress-3"} } { + variable _private + + Init $g + + bind zoom-$g <Enter> "focus %W" + bind zoom-$g <KeyPress-Escape> { blt::ZoomStack::Reset %W } + bind zoom-$g <${start}> { blt::ZoomStack::SetPoint %W %x %y } + bind zoom-$g <${reset}> { + if { [%W inside %x %y] } { + blt::ZoomStack::Reset %W + } + } + blt::AddBindTag $g zoom-$g +} + +proc blt::ZoomStack::ClickRelease { g } { + variable _private + + Init $g + bind zoom-$g <Enter> "focus %W" + bind zoom-$g <KeyPress-Escape> { blt::ZoomStack::Reset %W } + bind zoom-$g <ButtonPress-1> { blt::ZoomStack::DragStart %W %x %y } + bind zoom-$g <B1-Motion> { blt::ZoomStack::DragMotion %W %x %y } + bind zoom-$g <ButtonRelease-1> { blt::ZoomStack::DragFinish %W %x %y } + bind zoom-$g <ButtonPress-3> { + if { [%W inside %x %y] } { + blt::ZoomStack::Reset %W + } + } + blt::AddBindTag $g zoom-$g +} + +proc blt::ZoomStack::GetCoords { g x y index } { + variable _private + if { [$g cget -invertxy] } { + set _private($g,$index,x) $y + set _private($g,$index,y) $x + } else { + set _private($g,$index,x) $x + set _private($g,$index,y) $y + } +} + +proc blt::ZoomStack::MarkPoint { g index } { + variable _private + + if { [llength [$g xaxis use]] > 0 } { + set x [$g xaxis invtransform $_private($g,$index,x)] + } else if { [llength [$g x2axis use]] > 0 } { + set x [$g x2axis invtransform $_private($g,$index,x)] + } + if { [llength [$g yaxis use]] > 0 } { + set y [$g yaxis invtransform $_private($g,$index,y)] + } else if { [llength [$g y2axis use]] > 0 } { + set y [$g y2axis invtransform $_private($g,$index,y)] + } + set marker "zoomText_$index" + set text [format "x=%.4g\ny=%.4g" $x $y] + + if [$g marker exists $marker] { + $g marker configure $marker -coords { $x $y } -text $text + } else { + $g marker create text -coords { $x $y } -name $marker \ + -font "mathmatica1 10" \ + -text $text -anchor center -bg {} -justify left + } +} + +proc blt::ZoomStack::DestroyTitle { g } { + variable _private + + if { $_private($g,corner) == "A" } { + catch { $g marker delete "zoomTitle" } + } +} + +proc blt::ZoomStack::Pop { g } { + variable _private + + set zoomStack $_private($g,stack) + if { [llength $zoomStack] > 0 } { + set cmd [lindex $zoomStack 0] + set _private($g,stack) [lrange $zoomStack 1 end] + eval $cmd + TitleLast $g + blt::busy hold $g + update + blt::busy release $g + after 2000 [list blt::ZoomStack::DestroyTitle $g] + } else { + catch { $g marker delete "zoomTitle" } + } +} + +# Push the old axis limits on the stack and set the new ones + +proc blt::ZoomStack::Push { g } { + variable _private + + eval $g marker delete [$g marker names "zoom*"] + if { [info exists _private($g,afterId)] } { + after cancel $_private($g,afterId) + } + set x1 $_private($g,A,x) + set y1 $_private($g,A,y) + set x2 $_private($g,B,x) + set y2 $_private($g,B,y) + + if { ($x1 == $x2) || ($y1 == $y2) } { + # No delta, revert to start + return + } + set cmd {} + foreach axis [$g axis names] { + if { [$g axis cget $axis -hide] } { + continue + } + set min [$g axis cget $axis -min] + set max [$g axis cget $axis -max] + set logscale [$g axis cget $axis -logscale] + # Save the current scale (log or linear) so that we can restore it. + # This is for the case where the user changes to logscale while + # zooming. A previously pushed axis limit could be negative. It + # seems better for popping the zoom stack to restore a previous view + # (not convert the ranges). + set c [list $g axis configure $axis] + lappend c -min $min -max $max -logscale $logscale + append cmd "$c\n" + } + + # This effectively pushes the command to reset the graph to the current + # zoom level onto the stack. This is useful if the new axis ranges are + # bad and we need to reset the zoom stack. + set _private($g,stack) [linsert $_private($g,stack) 0 $cmd] + foreach axis [$g axis names] { + if { [$g axis cget $axis -hide] } { + continue; # Don't set zoom on axes not displayed. + } + set type [$g axis type $axis] + if { $type == "x" } { + set min [$g axis invtransform $axis $x1] + set max [$g axis invtransform $axis $x2] + } elseif { $type == "y" } { + set min [$g axis invtransform $axis $y1] + set max [$g axis invtransform $axis $y2] + } else { + continue; # Axis is not bound to any margin. + } + if { ![SetAxisRanges $g $axis $min $max] } { + Pop $g + bell + return + } + } + blt::busy hold $g + update; # This "update" redraws the graph + blt::busy release $g +} + +proc blt::ZoomStack::SetAxisRanges { g axis min max } { + if { $min > $max } { + set tmp $max; set max $min; set min $tmp + } + if { [catch { $g axis configure $axis -min $min -max $max }] != 0 } { + return 0 + } + return 1 +} + +# +# This routine terminates either an existing zoom, or pops back to +# the previous zoom level (if no zoom is in progress). +# +proc blt::ZoomStack::Reset { g } { + variable _private + + if { ![info exists _private($g,corner)] } { + Init $g + } + eval $g marker delete [$g marker names "zoom*"] + + if { $_private($g,corner) == "A" } { + # Reset the whole axis + Pop $g + } else { + set _private($g,corner) A + blt::RemoveBindTag $g select-region-$g + } +} + +proc blt::ZoomStack::TitleNext { g } { + variable _private + + set level [expr [llength $_private($g,stack)] + 1] + if { [$g cget -invertxy] } { + set coords "Inf -Inf" + } else { + set coords "-Inf Inf" + } + $g marker create text -name "zoomTitle" -text "Zoom #$level" \ + -coords $coords -bindtags "" -anchor nw +} + +proc blt::ZoomStack::TitleLast { g } { + variable _private + + set level [llength $_private($g,stack)] + if { $level > 0 } { + $g marker create text -name "zoomTitle" -anchor nw \ + -text "Zoom #$level" + } +} + + +proc blt::ZoomStack::SetPoint { g x y } { + variable _private + if { ![info exists _private($g,corner)] } { + Init $g + } + GetCoords $g $x $y $_private($g,corner) + bind select-region-$g <Motion> { + blt::ZoomStack::GetCoords %W %x %y B + #blt::ZoomStack::MarkPoint $g B + blt::ZoomStack::Box %W + } + if { $_private($g,corner) == "A" } { + if { ![$g inside $x $y] } { + return + } + # First corner selected, start watching motion events + + #MarkPoint $g A + TitleNext $g + + blt::AddBindTag $g select-region-$g + set _private($g,corner) B + } else { + # Delete the modal binding + blt::RemoveBindTag $g select-region-$g + Push $g + set _private($g,corner) A + } +} + +proc blt::ZoomStack::DragStart { g x y } { + variable _private + if { ![info exists _private($g,corner)] } { + Init $g + } + GetCoords $g $x $y A + if { ![$g inside $x $y] } { + return + } + set _private(drag) 1 + TitleNext $g +} + +proc blt::ZoomStack::DragMotion { g x y } { + variable _private + + if { $_private(drag) } { + GetCoords $g $x $y B + set dx [expr abs($_private($g,B,x) - $_private($g,A,x))] + set dy [expr abs($_private($g,B,y) - $_private($g,A,y))] + Box $g + if { $dy > 10 && $dx > 10 } { + return 1 + } + } + return 0 +} + +proc blt::ZoomStack::DragFinish { g x y } { + variable _private + if { [DragMotion $g $x $y] } { + Push $g + } else { + eval $g marker delete [$g marker names "zoom*"] + if { [info exists _private($g,afterId)] } { + after cancel $_private($g,afterId) + } + } + set _private(drag) 0 +} + + +proc blt::ZoomStack::MarchingAnts { g offset } { + variable _private + + incr offset + # wrap the counter after 2^16 + set offset [expr $offset & 0xFFFF] + if { [$g marker exists zoomOutline] } { + $g marker configure zoomOutline -dashoffset $offset + set interval $_private($g,interval) + set id [after $interval [list blt::ZoomStack::MarchingAnts $g $offset]] + set _private($g,afterId) $id + } +} + +proc blt::ZoomStack::Box { g } { + variable _private + + if { $_private($g,A,x) > $_private($g,B,x) } { + set x1 [$g xaxis invtransform $_private($g,B,x)] + set y1 [$g yaxis invtransform $_private($g,B,y)] + set x2 [$g xaxis invtransform $_private($g,A,x)] + set y2 [$g yaxis invtransform $_private($g,A,y)] + } else { + set x1 [$g xaxis invtransform $_private($g,A,x)] + set y1 [$g yaxis invtransform $_private($g,A,y)] + set x2 [$g xaxis invtransform $_private($g,B,x)] + set y2 [$g yaxis invtransform $_private($g,B,y)] + } + set coords { $x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1 } + if { [$g marker exists "zoomOutline"] } { + $g marker configure "zoomOutline" -coords $coords + } else { + set X [lindex [$g xaxis use] 0] + set Y [lindex [$g yaxis use] 0] + $g marker create line -coords $coords -name "zoomOutline" \ + -mapx $X -mapy $Y + set interval $_private($g,interval) + set id [after $interval [list blt::ZoomStack::MarchingAnts $g 0]] + set _private($g,afterId) $id + } +} + + +proc Blt_PostScriptDialog { g } { + set top $g.top + toplevel $top + + foreach var { center landscape maxpect preview decorations padx + pady paperwidth paperheight width height colormode } { + global $g.$var + set $g.$var [$g postscript cget -$var] + } + set row 1 + set col 0 + label $top.title -text "PostScript Options" + blt::table $top $top.title -cspan 7 + foreach bool { center landscape maxpect preview decorations } { + set w $top.$bool-label + label $w -text "-$bool" -font "courier 12" + blt::table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 } + set w $top.$bool-yes + global $g.$bool + radiobutton $w -text "yes" -variable $g.$bool -value 1 + blt::table $top $row,$col+1 $w -anchor w + set w $top.$bool-no + radiobutton $w -text "no" -variable $g.$bool -value 0 + blt::table $top $row,$col+2 $w -anchor w + incr row + } + label $top.modes -text "-colormode" -font "courier 12" + blt::table $top $row,0 $top.modes -anchor e -pady { 2 0 } -padx { 0 4 } + set col 1 + foreach m { color greyscale } { + set w $top.$m + radiobutton $w -text $m -variable $g.colormode -value $m + blt::table $top $row,$col $w -anchor w + incr col + } + set row 1 + frame $top.sep -width 2 -bd 1 -relief sunken + blt::table $top $row,3 $top.sep -fill y -rspan 6 + set col 4 + foreach value { padx pady paperwidth paperheight width height } { + set w $top.$value-label + label $w -text "-$value" -font "courier 12" + blt::table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 } + set w $top.$value-entry + global $g.$value + entry $w -textvariable $g.$value -width 8 + blt::table $top $row,$col+1 $w -cspan 2 -anchor w -padx 8 + incr row + } + blt::table configure $top c3 -width .125i + button $top.cancel -text "Cancel" -command "destroy $top" + blt::table $top $row,0 $top.cancel -width 1i -pady 2 -cspan 3 + button $top.reset -text "Reset" -command "destroy $top" + #blt::table $top $row,1 $top.reset -width 1i + button $top.print -text "Print" -command "blt::ResetPostScript $g" + blt::table $top $row,4 $top.print -width 1i -pady 2 -cspan 2 +} + +proc blt::ResetPostScript { g } { + foreach var { center landscape maxpect preview decorations padx + pady paperwidth paperheight width height colormode } { + global $g.$var + set old [$g postscript cget -$var] + if { [catch {$g postscript configure -$var [set $g.$var]}] != 0 } { + $g postscript configure -$var $old + set $g.$var $old + } + } + $g postscript output "out.ps" + puts stdout "wrote file \"out.ps\"." + flush stdout +} |