4 namespace eval ::utils::graph {}
31 set ::utils::graph::_options(graph) {
32 width height xtop ytop background font ticksize textgap xtick ytick
33 xmin xmax ymin ymax canvas vline hline textcolor tickcolor
36 set ::utils::graph::_defaults(graph) \
37 { -width 400 -height 300 -xtop 50 -ytop 30 -ticksize 5 -textgap 2 \
38 -xtick 5 -ytick 5 -tickcolor black -font fixed -background white \
39 -canvas {} -hline {} -vline {} -textcolor black \
40 -xlabels {} -ylabels {} -brect {} }
56 set ::utils::graph::_options(data) {
57 points lines bars color outline radius linewidth barwidth coords key
59 set ::utils::graph::_defaults(data) \
60 { -points 0 -lines 1 -bars 0 -color red -outline black -radius 2 \
61 -linewidth 1 -barwidth 1.0 -key {} -coords {} }
63 set ::utils::graph::_graphs {}
64 array set ::utils::graph::_data {}
71 proc ::utils::graph::create args {
72 set graph [
lindex $args 0]
73 lappend ::utils::graph::_graphs $graph
76 foreach key [
array names ::utils::graph::_data] {
77 if {[
string match "$graph,*" $key]} { unset ::utils::graph::_data($key)}
79 set ::utils::graph::_data($graph,sets) {}
81 set args [
concat graph $graph $::utils::graph::_defaults(graph) [
lrange $args 1 end]]
82 set extraArgs [
eval "::utils::graph::_configure $args"]
83 if {$extraArgs != ""} {
84 error "Unrecognised arguments: $extraArgs"
93 proc ::utils::graph::delete {graph} {
95 set index [
lindex $::utils::graph::_graphs $graph]
96 if {$index < 0} {
return}
97 set ::utils::graph::_graphs [
lreplace $::utils::graph::_graphs $index $index]
99 foreach key [
array names ::utils::graph::_data] {
100 if {[
string match "$graph,*" $key]} {
101 unset ::utils::graph::_data($key)
110 proc ::utils::graph::isgraph {graph} {
111 if {[lsearch $::utils::graph::_graphs $graph] >= 0} {
return 1}
119 proc ::utils::graph::data args {
122 set graph [
lindex $args 0]
123 set dataset [
lindex $args 1]
125 set args [
concat data $graph,$dataset $_defaults(data) \
126 [
lrange $args 2 end]]
128 set extraArgs [
eval "::utils::graph::_configure $args"]
129 if {$extraArgs != ""} {
130 error "Unrecognised graph data options: $extraArgs"
133 set marklist $_data($graph,sets)
134 if {[lsearch -exact $marklist $dataset] < 0} {
135 lappend _data($graph,sets) $dataset
139 set ncoords [
llength $_data($graph,$dataset,coords)]
140 if {$ncoords % 2 != 0} {
141 error "Error: coordinates list must have an even length"
152 proc ::utils::graph::cget {graph opt} {
155 if {[
string index $opt 0] == "-"} {
set opt [
string range $opt 1 end]}
160 if {! [
info exists _data($graph,$opt)]} {
161 error "No such graph option: $opt"
163 return $_data($graph,$opt)
169 proc ::utils::graph::configure args {
170 set newargs [
concat "graph" [
lindex $args 0] [
lrange $args 1 end]]
171 eval "::utils::graph::_configure $newargs"
181 proc ::utils::graph::_configure args {
183 set type [
lindex $args 0]
184 set dataset [
lindex $args 1]
185 set args [
lrange $args 2 end]
187 set optionList $::utils::graph::_options($type)
190 if {[
llength $args] % 2} { error "Error: odd-length options list: $args"}
192 for {
set i 0} {$i < [
llength $args]} {
incr i 2} {
193 set option [
lindex $args $i]
194 if {[
string index $option 0] != "-"} {
return [
lrange $args $i end]}
195 set option [
string range $option 1 end]
196 if {[lsearch $optionList $option] >= 0} {
197 set _data($dataset,$option) [
lindex $args [
expr {$i + 1}]]
205 proc ::utils::graph::redraw {graph} {
207 if {! [
info exists ::utils::graph::_data($graph,canvas)]} {
return}
208 $::utils::graph::_data($graph,canvas) delete -withtag g$graph
216 proc ::utils::graph::plot_axes {graph} {
222 set xmin $_data($graph,axmin)
223 set xmax $_data($graph,axmax)
224 set ymin $_data($graph,aymin)
225 set ymax $_data($graph,aymax)
227 set xminc [
xmap $graph $xmin]
228 set xmaxc [
xmap $graph $xmax]
229 set yminc [
ymap $graph $ymin]
230 set ymaxc [
ymap $graph $ymax]
232 set fontsize [font configure font_Small -size]
233 if { $ymax > 99999 } {
set xminc [
expr {$xminc + $fontsize}]}
234 if { $ymax > 999999 } {
set xminc [
expr {$xminc + $fontsize}]}
236 set canvas $_data($graph,canvas)
240 foreach attr {ticksize font textcolor tickcolor textgap \
241 xtick ytick xlabels ylabels} {
242 set $attr $_data($graph,$attr)
245 $canvas create rectangle $xminc $yminc $xmaxc $ymaxc -outline $tickcolor \
246 -fill $_data($graph,background) -tag $tag
248 set brect $_data($graph,brect)
249 for {
set i 0} {$i < [
llength $brect]} {
incr i} {
250 set item [
lindex $brect $i]
251 set x1 [
xmap $graph [
lindex $item 0]]
252 set y1 [
ymap $graph [
lindex $item 1]]
253 set x2 [
xmap $graph [
lindex $item 2]]
254 set y2 [
ymap $graph [
lindex $item 3]]
255 if {$x1 < $xminc} {
set x1 $xminc}
256 if {$x1 > $xmaxc} {
set x1 $xmaxc}
257 if {$x2 < $xminc} {
set x2 $xminc}
258 if {$x2 > $xmaxc} {
set x2 $xmaxc}
259 if {$y1 > $yminc} {
set y1 $yminc}
260 if {$y1 < $ymaxc} {
set y1 $ymaxc}
261 if {$y2 > $yminc} {
set y2 $yminc}
262 if {$y2 < $ymaxc} {
set y2 $ymaxc}
263 $canvas create rectangle $x1 $y1 $x2 $y2 -fill [
lindex $item 4] -width 0 \
268 foreach vline $_data($graph,vline) {
269 set color [
lindex $vline 0]
270 set width [
lindex $vline 1]
271 set type [
lindex $vline 2]
272 set inc [
lindex $vline 3]
273 set xminvalue [
xmap $graph $xmin]
274 set xmaxvalue [
xmap $graph $xmax]
277 set xvalue [
xmap $graph $inc]
278 if {$xvalue != $xminvalue && $xvalue != $xmaxvalue} {
279 $canvas create line $xvalue $yminc $xvalue $ymaxc -width $width \
280 -fill $color -tag $tag
282 }
elseif {$inc > 0} {
284 set x [
expr {int($xmin/$inc) * $inc + $inc}]
286 set xvalue [
xmap $graph $x]
287 if {$xvalue != $xminvalue && $xvalue != $xmaxvalue} {
288 $canvas create line $xvalue $yminc $xvalue $ymaxc -width $width \
289 -fill $color -tag $tag
291 set x [
expr {$x + $inc}]
297 foreach hline $_data($graph,hline) {
298 set color [
lindex $hline 0]
299 set width [
lindex $hline 1]
300 set type [
lindex $hline 2]
301 set inc [
lindex $hline 3]
302 set yminvalue [
ymap $graph $ymin]
303 set ymaxvalue [
ymap $graph $ymax]
305 set yvalue [
ymap $graph $inc]
306 if {$yvalue != $yminvalue && $yvalue != $ymaxvalue} {
307 $canvas create line $xminc $yvalue $xmaxc $yvalue -width $width \
308 -fill $color -tag $tag
310 }
elseif {$inc > 0} {
311 set y [
expr {int($ymin/$inc) * $inc + $inc}]
313 set yvalue [
ymap $graph $y]
314 if {$yvalue != $yminvalue && $yvalue != $ymaxvalue} {
315 $canvas create line $xminc $yvalue $xmaxc $yvalue -width $width \
316 -fill $color -tag $tag
318 set y [
expr {$y + $inc}]
324 set nxlabels [
llength $xlabels]
325 set nylabels [
llength $ylabels]
328 set x [
expr {int($xmin/$xtick) * $xtick}]
329 while {$x < $xmin} {
set x [
expr {$x + $xtick}]}
330 while {$x <= $xmax} {
331 set xc [
xmap $graph $x]
332 $canvas create line $xc $yminc $xc [
expr {$yminc - $ticksize}] \
333 -tag $tag -fill $tickcolor
334 $canvas create line $xc $ymaxc $xc [
expr {$ymaxc + $ticksize}] \
335 -tag $tag -fill $tickcolor
336 if {$nxlabels == 0} {
337 $canvas create text $xc [
expr {$yminc + $textgap}] -font $font \
340 set x [
expr {$x + $xtick}]
343 for {
set i 0} {$i < $nxlabels} {
incr i} {
344 set label [
lindex $xlabels $i]
345 set x [
lindex $label 0]
346 set text [
lindex $label 1]
347 set xc [
xmap $graph $x]
348 $canvas create text $xc [
expr {$yminc + $textgap}] -font $font \
349 -text $text -anchor n -tag $tag -fill $textcolor -justify center
353 set y [
expr {int($ymin/$ytick) * $ytick}]
354 while {$y < $ymin} {
set y [
expr {$y + $ytick}]}
355 while {$y <= $ymax} {
356 set yc [
ymap $graph $y]
357 $canvas create line $xminc $yc [
expr {$xminc + $ticksize}] $yc \
358 -tag $tag -fill $tickcolor
359 $canvas create line [
expr {$xmaxc - $ticksize}] $yc $xmaxc $yc \
360 -tag $tag -fill $tickcolor
361 if {$nylabels == 0} {
362 $canvas create text [
expr {$xminc - $textgap}] $yc -font $font \
365 set y [
expr {$y + $ytick}]
368 for {
set i 0} {$i < $nylabels} {
incr i} {
369 set label [
lindex $ylabels $i]
370 set y [
lindex $label 0]
371 set text [
lindex $label 1]
372 set yc [
ymap $graph $y]
373 $canvas create text [
expr {$xminc - $textgap}] $yc -font $font \
374 -text $text -anchor e -tag $tag -fill $textcolor
381 proc ::utils::graph::plot_data {graph} {
383 set canvas $_data($graph,canvas)
385 foreach dataset $_data($graph,sets) {
386 set color $_data($graph,$dataset,color)
387 set outline $_data($graph,$dataset,outline)
389 set coords [
scale_data $graph $_data($graph,$dataset,coords)]
390 set ncoords [
expr {[llength $coords] - 1}]
393 if {$_data($graph,$dataset,key) != ""} {
394 set key $_data($graph,$dataset,key)
398 set x [
expr {[lindex $coords 0] + 3}]
399 set y [
lindex $coords 1]
401 set nexty [
lindex $coords 3]
402 if {$nexty > $y} {
set dy -3
set anchor sw}
405 catch {$canvas create text $x $y -fill $color -tag $tag \
406 -text $_data($graph,$dataset,key) \
407 -font $_data($graph,font) -anchor $anchor}
412 if {$_data($graph,$dataset,lines)} {
414 catch {
eval "$canvas create line $coords -fill $color \
415 -width $_data($graph,$dataset,linewidth) -tag $tag"}
419 if {$_data($graph,$dataset,points)} {
420 for {
set i 0} {$i < $ncoords} {
incr i 2} {
421 set x [
lindex $coords $i]
422 set y [
lindex $coords [
expr {$i + 1}]]
423 set p $_data($graph,$dataset,radius)
424 $canvas create oval [
expr {$x-$p}] [
expr {$y-$p}] \
425 [
expr {$x+$p}] [
expr {$y+$p}] \
426 -fill $color -outline $outline -width 1 -tag $tag
431 if {$_data($graph,$dataset,bars)} {
432 set base [
ymap $graph $_data($graph,aymin)]
434 if {$_data($graph,$dataset,bars) == 2} {
435 set base [
expr {int(($_data($graph,aymax) - 0) * $_data($graph,yfac) + $_data($graph,ytop))}]
437 set hwidth [
xmap $graph $_data($graph,$dataset,barwidth)]
438 set hwidth [
expr {$hwidth - [xmap $graph 0]}]
439 set hwidth [
expr {$hwidth / 2}]
440 if {$hwidth < 1} {
set hwidth 1}
442 for {
set i 0} {$i < $ncoords} {
incr i 2} {
443 set x [
lindex $coords $i]
444 set y [
lindex $coords [
expr {$i + 1}]]
445 if {$_data($graph,$dataset,bars) == 2} {
448 set x1 [
expr {$x+(($i+1)%2)*$hwidth-1}]
449 set x [
expr {$x-($i % 2)*$hwidth+1}]
451 set x1 [
expr {$x+(($i+1)%2)*$hwidth}]
452 set x [
expr {$x-($i % 2)*$hwidth}]
455 set x1 [
expr {$x+$hwidth}]
456 set x [
expr {$x-$hwidth}]
458 $canvas create rectangle \
460 -fill $color -outline $outline -tag $tag
473 proc ::utils::graph::round {n} {
474 set intn [
expr {int($n)}]
475 if {[
expr {$n - $intn}] < 0.1 && [
expr {$intn - $n}] < 0.1} {
476 return [
expr {round($n)}]
478 return [
expr {double(round($n * 10.0)) / 10.0}]
487 proc ::utils::graph::point_visible {graph x y} {
489 set xmin $_data($graph,xtop)
490 set ymin $_data($graph,ytop)
491 set xmax [
expr {$xmin + $_data($graph,width)}]
492 set ymax [
expr {$ymin + $_data($graph,height)}]
494 if {$x >= $xmin && $x <= $xmax && $y >= $ymin && $y <= $ymax} {
return 1}
502 proc ::utils::graph::rescale {graph} {
504 set width $_data($graph,width)
505 set height $_data($graph,height)
506 set xdelta [
expr {double($_data($graph,axmax) - $_data($graph,axmin))}]
507 set ydelta [
expr {double($_data($graph,aymax) - $_data($graph,aymin))}]
509 if {$xdelta < 0.0001} {
set xdelta 0.0001}
510 if {$ydelta < 0.0001} {
set ydelta 0.0001}
512 set _data($graph,xfac) [
expr {double($width)/$xdelta}]
513 set _data($graph,yfac) [
expr {double($height)/$ydelta}]
520 proc ::utils::graph::xmap {graph x} {
522 return [
expr {int(($x - $_data($graph,axmin)) * \
523 $_data($graph,xfac) + $_data($graph,xtop))}]
529 proc ::utils::graph::ymap {graph y} {
531 if {$y == ""} { error "y is empty"}
532 return [
expr {int(($_data($graph,aymax) - $y) * \
533 $_data($graph,yfac) + $_data($graph,ytop))}]
539 proc ::utils::graph::xunmap {graph cx} {
541 return [
expr {$_data($graph,axmin) + \
542 double($cx - $_data($graph,xtop)) / \
543 double($_data($graph,xfac))}]
549 proc ::utils::graph::yunmap {graph cy} {
551 return [
expr {$_data($graph,aymax) - \
552 double($cy - $_data($graph,ytop)) / \
553 double($_data($graph,yfac))}]
559 proc ::utils::graph::scale_data {graph coords} {
561 for {
set i 0} {$i < [
llength $coords] - 1} {
incr i 2} {
562 lappend result [
xmap $graph [
lindex $coords $i]]
563 lappend result [
ymap $graph [
lindex $coords [
expr {$i + 1}]]]
571 proc ::utils::graph::set_range {graph} {
574 set xmin 1000000000
set xmax -100000000
575 set ymin 1000000000
set ymax -100000000
577 foreach dataset $_data($graph,sets) {
578 set coords $_data($graph,$dataset,coords)
579 for {
set i 0} {$i < [
llength $coords] - 1} {
incr i 2} {
580 set x [
lindex $coords $i]
581 set y [
lindex $coords [
expr {$i + 1}]]
583 if {$x < $xmin} {
set xmin $x}
584 if {$x > $xmax} {
set xmax $x}
585 if {$y < $ymin} {
set ymin $y}
586 if {$y > $ymax} {
set ymax $y}
591 if {$xmax < $xmin} {
set xmin 0
set xmax 1}
592 if {$ymax < $ymin} {
set ymin 0
set ymax 1}
594 set xtick $_data($graph,xtick)
595 set ytick $_data($graph,ytick)
596 set _data($graph,axmin) [
expr {floor($xmin/$xtick) * $xtick}]
597 set _data($graph,axmax) [
expr {floor($xmax/$xtick) * $xtick + $xtick}]
598 set _data($graph,aymin) [
expr {floor($ymin/$ytick) * $ytick}]
599 set _data($graph,aymax) [
expr {floor($ymax/$ytick) * $ytick + $ytick}]
602 foreach coord {xmin xmax ymin ymax} {
603 if {[
info exists _data($graph,$coord)]} {
604 set _data($graph,a$coord) $_data($graph,$coord)