Dave Griffin (1-Aug-2003) writes: I recently needed to dynamically generate some graphics for inclusion on a web page. There are a couple of techniques for doing this with various tradeoffs: * HTML-based (div/gif magic) charts are quick and dirty, but are difficult to make look professional. * Generating charts with packages like .... are a good option, but require compiled extensions. * [BLT] is an obvious choice, and combined with Img you can snag nice graphs and convert them to JPEG or GIF, but the application must be able to map the window briefly to grab the pixels and this is less than acceptable on a server. Generating postscript from BLT and rendering it via Ghostscript is an option, but introduces other packaging problems. * We have chosen to use SVG as the rendering engine on the browser and to generate the graphs using the canvas widget and the handy canv2svg routines. All this will be wrapped up in a Starkit which will be running as a daemon with the root window withdrawn. Anyway, there are many examples of drawing line and bar charts in this Wiki, and at one level it is pretty simple to produce a custom graphic. When you get into the more general-purpose stuff, it gets a bit harder (as I'm sure George Howlett could attest). As I build this, I'm going to keep track of some of the support routines I'm developing to create the charting package. I hope that I do this in a way that you can use other examples here in the Wiki and combine them with these routines to create better charts and graphs. Comments and improvements welcome (neither will be hard to come by). namespace eval chart {} # # nice_number # # Reference: Paul Heckbert, "Nice Numbers for Graph Labels", # Graphics Gems, pp 61-63. # # Finds a "nice" number approximately equal to x. # # Args: x -- target number # round -- If non-zero, round. Otherwise take ceiling of value. proc chart::nice_number {x round} { # expt -- Exponent of x # frac -- Fractional part of x # nice -- Nice, rounded fraction set expt [expr floor(log10($x))] set frac [expr $x / pow(10.0, double($expt))] if ($round) { if {$frac < 1.5} { set nice 1.0 } elseif {$frac < 3.0} { set nice 2.0 } elseif {$frac < 7.0} { set nice 5.0 } else { set nice 10.0 } } else { if {$frac <= 1.0} { set nice 1.0 } elseif {$frac <= 2.0} { set nice 2.0 } elseif {$frac <= 5.0} { set nice 5.0 } else { set nice 10.0 } } return [expr $nice * pow(10.0, double($expt))] } # # loose_label # # Reference: Paul Heckbert, "Nice Numbers for Graph Labels", # Graphics Gems # # Returns a set of graph labelling attributes given a range of numbers # that need to be graphed. # # Args: min -- Lower range boundary # max -- Upper range boundary # steps -- Number of major tick marks desired # proc chart::loose_label {min max {steps 5}} { if {$steps < 2} { set steps 2 } # We expect min!=max # Try and be nice by raising max by 1. This obviously # fails miserably for small values. if {$min == $max} { set max [expr $max + 1] } set range [nice_number [expr $max - $min] 0] # tick mark spacing set d [nice_number [expr $range / ($steps - 1)] 1] set graphmin [expr floor($min/$d) * $d] set graphmax [expr ceil($max/$d) * $d] set nfx [expr int(-floor(log10($d)))] set nfrac [expr ($nfx > 0) ? $nfx : 0] set stepfmt [format "%%.%df" $nfrac] set ticks [list] for {set x $graphmin} {$x < [expr $graphmax + 0.5 * $d]} {set x [expr $x + $d]} { lappend ticks $x } return [list graphmin $graphmin graphmax $graphmax step $d stepfmt $stepfmt ticks $ticks] } # # range_and_step # # Given a range (and a maximum number of steps), returns a # "nice" range and "nice" step (tick) size # proc chart::range_and_step { range {steps 5}} { if {$steps < 2} { set steps 2 } set range [nice_number $range 0] set step [nice_number [expr $range / ($steps - 1)] 1] return [list $range $step] } ##############################################################################################3 proc draw_y_axis { canvas x y height axisProps {fgcolor black} } { array set ap $axisProps set y1 [expr $y - $height] $canvas create line $x $y $x $y1 set nTicks [llength $ap(ticks)] set tickIncr [expr $height / $nTicks] set ty $y foreach t $ap(ticks) { $canvas create line $x $ty [expr $x - 5] $ty $canvas create text [expr $x - 7] $ty -justify right -anchor e -text [format $ap(stepfmt) $t] set ty [expr $ty - $tickIncr] } } ---- See also [A little function plotter] | [Category Application] | [Category Graphics]