1# plotannot.tcl -- 2# Facilities for annotating charts 3# 4# Note: 5# This source file contains such functions as to draw a 6# balloon text in an xy-graph. 7# It is the companion of "plotchart.tcl" 8# 9 10# 11# Static data 12# 13namespace eval ::Plotchart { 14 # Index, three pairs of scale factors to determine xy-coordinates 15 set BalloonDir(north-west) {0 0 1 -2 -2 1 0} 16 set BalloonDir(north) {1 -1 0 0 -3 1 0} 17 set BalloonDir(north-east) {2 -1 0 2 -2 0 1} 18 set BalloonDir(east) {3 0 -1 3 0 0 1} 19 set BalloonDir(south-east) {4 0 -1 2 2 -1 0} 20 set BalloonDir(south) {5 1 0 0 3 -1 0} 21 set BalloonDir(south-west) {6 1 0 -2 2 0 -1} 22 set BalloonDir(west) {7 0 1 -3 0 0 -1} 23 24 set TextDir(centre) c 25 set TextDir(center) c 26 set TextDir(c) c 27 set TextDir(west) w 28 set TextDir(w) w 29 set TextDir(north-west) nw 30 set TextDir(nw) nw 31 set TextDir(north) n 32 set TextDir(n) n 33 set TextDir(north-east) ew 34 set TextDir(ne) ew 35 set TextDir(east) e 36 set TextDir(e) e 37 set TextDir(south-west) nw 38 set TextDir(sw) sw 39 set TextDir(south) s 40 set TextDir(s) s 41 set TextDir(south-east) ew 42 set TextDir(east) e 43} 44 45# DefaultBalloon -- 46# Set the default properties of balloon text and other types of annotation 47# Arguments: 48# w Name of the canvas 49# Result: 50# None 51# Side effects: 52# Stores the default settings 53# 54proc ::Plotchart::DefaultBalloon { w } { 55 variable settings 56 57 foreach {option value} {font fixed 58 margin 5 59 textcolour black 60 justify left 61 arrowsize 5 62 background white 63 outline black 64 rimwidth 1} { 65 set settings($w,balloon$option) $value 66 } 67 foreach {option value} {font fixed 68 colour black 69 justify left} { 70 set settings($w,text$option) $value 71 } 72} 73 74# ConfigBalloon -- 75# Configure the properties of balloon text 76# Arguments: 77# w Name of the canvas 78# args List of arguments 79# Result: 80# None 81# Side effects: 82# Stores the new settings for the next balloon text 83# 84proc ::Plotchart::ConfigBalloon { w args } { 85 variable settings 86 87 foreach {option value} $args { 88 set option [string range $option 1 end] 89 switch -- $option { 90 "font" - 91 "margin" - 92 "textcolour" - 93 "justify" - 94 "arrowsize" - 95 "background" - 96 "outline" - 97 "rimwidth" { 98 set settings($w,balloon$option) $value 99 } 100 "textcolor" { 101 set settings($w,balloontextcolour) $value 102 } 103 } 104 } 105} 106 107# ConfigPlainText -- 108# Configure the properties of plain text 109# Arguments: 110# w Name of the canvas 111# args List of arguments 112# Result: 113# None 114# Side effects: 115# Stores the new settings for the next plain text 116# 117proc ::Plotchart::ConfigPlainText { w args } { 118 variable settings 119 120 foreach {option value} $args { 121 set option [string range $option 1 end] 122 switch -- $option { 123 "font" - 124 "textcolour" - 125 "justify" { 126 set settings($w,text$option) $value 127 } 128 "textcolor" { 129 set settings($w,textcolour) $value 130 } 131 "textfont" { 132 # Ugly hack! 133 set settings($w,$option) $value 134 } 135 } 136 } 137} 138 139# DrawBalloon -- 140# Plot a balloon text in a chart 141# Arguments: 142# w Name of the canvas 143# x X-coordinate of the point the arrow points to 144# y Y-coordinate of the point the arrow points to 145# text Text in the balloon 146# dir Direction of the arrow (north, north-east, ...) 147# Result: 148# None 149# Side effects: 150# Text and polygon drawn in the chart 151# 152proc ::Plotchart::DrawBalloon { w x y text dir } { 153 variable settings 154 variable BalloonDir 155 156 # 157 # Create the item and then determine the coordinates 158 # of the frame around the text 159 # 160 set item [$w create text 0 0 -text $text -tag BalloonText \ 161 -font $settings($w,balloonfont) -fill $settings($w,balloontextcolour) \ 162 -justify $settings($w,balloonjustify)] 163 164 if { ![info exists BalloonDir($dir)] } { 165 set dir south-east 166 } 167 168 foreach {xmin ymin xmax ymax} [$w bbox $item] {break} 169 170 set xmin [expr {$xmin-$settings($w,balloonmargin)}] 171 set xmax [expr {$xmax+$settings($w,balloonmargin)}] 172 set ymin [expr {$ymin-$settings($w,balloonmargin)}] 173 set ymax [expr {$ymax+$settings($w,balloonmargin)}] 174 175 set xcentr [expr {($xmin+$xmax)/2}] 176 set ycentr [expr {($ymin+$ymax)/2}] 177 set coords [list $xmin $ymin \ 178 $xcentr $ymin \ 179 $xmax $ymin \ 180 $xmax $ycentr \ 181 $xmax $ymax \ 182 $xcentr $ymax \ 183 $xmin $ymax \ 184 $xmin $ycentr ] 185 186 set idx [lindex $BalloonDir($dir) 0] 187 set scales [lrange $BalloonDir($dir) 1 end] 188 189 set factor $settings($w,balloonarrowsize) 190 set extraCoords {} 191 192 set xbase [lindex $coords [expr {2*$idx}]] 193 set ybase [lindex $coords [expr {2*$idx+1}]] 194 195 foreach {xscale yscale} $scales { 196 set xnew [expr {$xbase+$xscale*$factor}] 197 set ynew [expr {$ybase+$yscale*$factor}] 198 lappend extraCoords $xnew $ynew 199 } 200 201 # 202 # Insert the extra coordinates 203 # 204 set coords [eval lreplace [list $coords] [expr {2*$idx}] [expr {2*$idx+1}] \ 205 $extraCoords] 206 207 set xpoint [lindex $coords [expr {2*$idx+2}]] 208 set ypoint [lindex $coords [expr {2*$idx+3}]] 209 210 set poly [$w create polygon $coords -tag BalloonFrame \ 211 -fill $settings($w,balloonbackground) \ 212 -width $settings($w,balloonrimwidth) \ 213 -outline $settings($w,balloonoutline)] 214 215 # 216 # Position the two items 217 # 218 foreach {xtarget ytarget} [coordsToPixel $w $x $y] {break} 219 set dx [expr {$xtarget-$xpoint}] 220 set dy [expr {$ytarget-$ypoint}] 221 $w move $item $dx $dy 222 $w move $poly $dx $dy 223 $w raise BalloonFrame 224 $w raise BalloonText 225} 226 227# DrawPlainText -- 228# Plot plain text in a chart 229# Arguments: 230# w Name of the canvas 231# x X-coordinate of the point the arrow points to 232# y Y-coordinate of the point the arrow points to 233# text Text to be drawn 234# anchor Anchor position (north, north-east, ..., defaults to centre) 235# Result: 236# None 237# Side effects: 238# Text drawn in the chart 239# 240proc ::Plotchart::DrawPlainText { w x y text {anchor centre} } { 241 variable settings 242 variable TextDir 243 244 foreach {xtext ytext} [coordsToPixel $w $x $y] {break} 245 246 if { [info exists TextDir($anchor)] } { 247 set anchor $TextDir($anchor) 248 } else { 249 set anchor c 250 } 251 252 $w create text $xtext $ytext -text $text -tag PlainText \ 253 -font $settings($w,textfont) -fill $settings($w,textcolour) \ 254 -justify $settings($w,textjustify) -anchor $anchor 255 256 $w raise PlainText 257} 258 259# BrightenColour -- 260# Compute a brighter colour 261# Arguments: 262# color Original colour 263# intensity Colour to interpolate with 264# factor Factor by which to brighten the colour 265# Result: 266# New colour 267# Note: 268# Adapted from R. Suchenwirths Wiki page on 3D bars 269# 270proc ::Plotchart::BrightenColour {color intensity factor} { 271 foreach i {r g b} n [winfo rgb . $color] d [winfo rgb . $intensity] f [winfo rgb . white] { 272 #checker exclude warnVarRef 273 set $i [expr {int(255.*($n+($d-$n)*$factor)/$f)}] 274 } 275 #checker exclude warnUndefinedVar 276 format #%02x%02x%02x $r $g $b 277} 278 279# DrawGradientBackground -- 280# Add a gradient background to the plot 281# Arguments: 282# w Name of the canvas 283# colour Main colour 284# dir Direction of the gradient (left-right, top-down, 285# bottom-up, right-left) 286# intensity Brighten (white) or darken (black) the colours 287# rect (Optional) coordinates of the rectangle to be filled 288# Result: 289# None 290# Side effects: 291# Gradient background drawn in the chart 292# 293proc ::Plotchart::DrawGradientBackground { w colour dir intensity {rect {}} } { 294 variable scaling 295 296 set pxmin $scaling($w,pxmin) 297 set pxmax $scaling($w,pxmax) 298 set pymin $scaling($w,pymin) 299 set pymax $scaling($w,pymax) 300 301 if { $rect != {} } { 302 foreach {rxmin rymin rxmax rymax} $rect {break} 303 } else { 304 set rxmin $pxmin 305 set rxmax $pxmax 306 set rymin $pymin 307 set rymax $pymax 308 } 309 310 switch -- $dir { 311 "left-right" { 312 set dir h 313 set first 0.0 314 set last 1.0 315 set fac [expr {($pxmax-$pxmin)/50.0}] 316 } 317 "right-left" { 318 set dir h 319 set first 1.0 320 set last 0.0 321 set fac [expr {($pxmax-$pxmin)/50.0}] 322 } 323 "top-down" { 324 set dir v 325 set first 0.0 326 set last 1.0 327 set fac [expr {($pymin-$pymax)/50.0}] 328 } 329 "bottom-up" { 330 set dir v 331 set first 1.0 332 set last 0.0 333 set fac [expr {($pymin-$pymax)/50.0}] 334 } 335 default { 336 set dir v 337 set first 0.0 338 set last 1.0 339 set fac [expr {($pymin-$pymax)/50.0}] 340 } 341 } 342 343 if { $dir == "h" } { 344 set x2 $rxmin 345 set y1 $rymin 346 set y2 $rymax 347 } else { 348 set y2 $rymax 349 set x1 $rxmin 350 set x2 $rxmax 351 } 352 353 set n 50 354 if { $dir == "h" } { 355 set nmax [expr {ceil($n*($rxmax-$rxmin)/double($pxmax-$pxmin))}] 356 } else { 357 set nmax [expr {ceil($n*($rymin-$rymax)/double($pymin-$pymax))}] 358 } 359 for { set i 0 } { $i < $nmax } { incr i } { 360 set factor [expr {($first*$i+$last*($n-$i-1))/double($n)}] 361 set gcolour [BrightenColour $colour $intensity $factor] 362 363 if { $dir == "h" } { 364 set x1 $x2 365 set x2 [expr {$rxmin+($i+1)*$fac}] 366 if { $i == $nmax-1 } { 367 set x2 $rxmax 368 } 369 } else { 370 set y1 $y2 371 set y2 [expr {$rymax+($i+1)*$fac}] 372 if { $i == $nmax-1 } { 373 set y2 $rymin 374 } 375 } 376 377 $w create rectangle $x1 $y1 $x2 $y2 -fill $gcolour -outline $gcolour -tag {data background} 378 } 379 380 $w lower data 381 $w lower background 382} 383 384# DrawImageBackground -- 385# Add an image (tilde) to the background to the plot 386# Arguments: 387# w Name of the canvas 388# colour Main colour 389# image Name of the image 390# Result: 391# None 392# Side effects: 393# Image appears in the plot area, tiled if needed 394# 395proc ::Plotchart::DrawImageBackground { w image } { 396 variable scaling 397 398 set pxmin $scaling($w,pxmin) 399 set pxmax $scaling($w,pxmax) 400 set pymin $scaling($w,pymin) 401 set pymax $scaling($w,pymax) 402 403 set iwidth [image width $image] 404 set iheight [image height $image] 405 406 for { set y $pymax } { $y > $pymin } { set y [expr {$y-$iheight}] } { 407 for { set x $pxmin } { $x < $pxmax } { incr x $iwidth } { 408 $w create image $x $y -image $image -anchor sw -tags {data background} 409 } 410 } 411 412 $w lower data 413 $w lower background 414} 415