if { 0 } { Arjen Markus (4 february 2004) I wrote this little script as a preliminary exercise to a small application I have in mind, that being one that addresses an engineering problem (I am, after all, an engineer who has not done much engineering lately, being professionally preoccupied with software development and maintenance :).
Now, this little script produces a very simple graph and generates two timeseries that are to be shown in that graph. The x-axis will adjust itself over time moving the shown data in the right direction. I think this type of graph is called a slipchart, whence the name.
2004-5-2 SRIV: It's a stripchart. Well done.
Much of this little script can be made more general and put into Tklib, IMO.
AM I have elaborated that idea - see Plots and charts
And of course, it can be made prettier too. }
# slipchart.tcl -- # Facilities to draw a slipchart in a dedicated canvas # # Slipchart -- # Namespace to hold the procedures and the private data # namespace eval ::Slipchart { variable scaling variable data_series namespace export worldCoordinates viewPort createSlipchart \ coordsToPixel } # viewPort -- # Set the pixel extremes for the graph # Arguments: # w Name of the canvas window # pxmin Minimum X-coordinate # pxmax Maximum X-coordinate # pymin Minimum Y-coordinate # pymax Maximum Y-coordinate # Result: # None # Side effect: # Array scaling filled # proc ::Slipchart::viewPort { w pxmin pxmax pymin pymax } { variable scaling set scaling($w,pxmin) $pxmin set scaling($w,pymin) $pymin set scaling($w,pxmax) $pxmax set scaling($w,pymax) $pymax set scaling($w,new) 1 } # worldCoordinates -- # Set the extremes for the world coordinates # Arguments: # w Name of the canvas window # width Width of the canvas window # height Height of the canvas window # xmin Minimum X-coordinate # xmax Maximum X-coordinate # ymin Minimum Y-coordinate # ymax Maximum Y-coordinate # Result: # None # Side effect: # Array scaling filled # proc ::Slipchart::worldCoordinates { w xmin xmax ymin ymax } { variable scaling set scaling($w,xmin) $xmin set scaling($w,ymin) $ymin set scaling($w,xmax) $xmax set scaling($w,ymax) $ymax set scaling($w,new) 1 } # coordsToPixel -- # Convert world coordinates to pixel coordinates # Arguments: # w Name of the canvas # xcrd X-coordinate # ycrd Y-coordinate # Result: # List of two elements, x- and y-coordinates in pixels # proc ::Slipchart::coordsToPixel { w xcrd ycrd } { variable scaling if { $scaling($w,new) == 1 } { set scaling($w,new) 0 set width [expr {$scaling($w,pxmax)-$scaling($w,pxmin)+1}] set height [expr {$scaling($w,pymax)-$scaling($w,pymin)+1}] set dx [expr {$scaling($w,xmax)-$scaling($w,xmin)}] set dy [expr {$scaling($w,ymax)-$scaling($w,ymin)}] set scaling($w,xfactor) [expr {$width/$dx}] set scaling($w,yfactor) [expr {$height/$dy}] } set xpix [expr {$scaling($w,pxmin)+($xcrd-$scaling($w,xmin))*$scaling($w,xfactor)}] set ypix [expr {$scaling($w,pymin)+($scaling($w,ymax)-$ycrd)*$scaling($w,yfactor)}] return [list $xpix $ypix] } # createSlipchart -- # Create a command for drawing a slipchart # Arguments: # w Name of the canvas # xscale Minimum, maximum and step for x-axis (initial) # yscale Minimum, maximum and step for y-axis # Result: # Name of a new command # Note: # The entire canvas will be dedicated to the slipchart. # The slipchart will be drawn with axes # proc ::Slipchart::createSlipchart { w xscale yscale } { variable data_series foreach s [array names data_series "$w,*"] { unset data_series($s) } set newchart "slipchart_$w" interp alias {} $newchart {} ::Slipchart::DrawData $w set pxmin 80 set pymin 20 set pxmax [expr {[$w cget -width] - 40}] set pymax [expr {[$w cget -height] - 20}] foreach {xmin xmax xdelt} $xscale {break} foreach {ymin ymax ydelt} $yscale {break} viewPort $w $pxmin $pxmax $pymin $pymax worldCoordinates $w $xmin $xmax $ymin $ymax DrawYaxis $w $ymin $ymax $ydelt DrawXaxis $w $xmin $xmax $xdelt DrawMask $w return $newchart } # DrawYaxis -- # Draw the y-axis # Arguments: # w Name of the canvas # ymin Minimum y coordinate # ymax Maximum y coordinate # ystep Step size # Result: # None # Side effects: # Axis drawn in canvas # proc ::Slipchart::DrawYaxis { w ymin ymax ydelt } { variable scaling $w create line $scaling($w,pxmin) $scaling($w,pymin) \ $scaling($w,pxmin) $scaling($w,pymax) \ -fill black -tag yaxis set y $ymin while { $y < $ymax+0.5*$ydelt } { foreach {xcrd ycrd} [coordsToPixel $w $scaling($w,xmin) $y] {break} $w create text $xcrd $ycrd -text $y -tag yaxis -anchor e set y [expr {$y+$ydelt}] } } # DrawXaxis -- # Draw the x-axis # Arguments: # w Name of the canvas # xmin Minimum x coordinate # xmax Maximum x coordinate # xstep Step size # Result: # None # Side effects: # Axis drawn in canvas # proc ::Slipchart::DrawXaxis { w xmin xmax xdelt } { variable scaling $w delete xaxis $w create line $scaling($w,pxmin) $scaling($w,pymax) \ $scaling($w,pxmax) $scaling($w,pymax) \ -fill black -tag xaxis set x $xmin while { $x < $xmax+0.5*$xdelt } { foreach {xcrd ycrd} [coordsToPixel $w $x $scaling($w,ymin)] {break} $w create text $xcrd $ycrd -text $x -tag xaxis -anchor n set x [expr {$x+$xdelt}] } set scaling($w,xdelt) $xdelt } # DrawMask -- # Draw the stuff that masks the data lines outside the graph # Arguments: # w Name of the canvas # Result: # None # Side effects: # Several polygons drawn in the background colour # proc ::Slipchart::DrawMask { w } { variable scaling set width [$w cget -width] set height [expr {[$w cget -height] + 1}] set colour [$w cget -background] set pxmin $scaling($w,pxmin) set pxmax $scaling($w,pxmax) set pymin $scaling($w,pymin) set pymax $scaling($w,pymax) $w create rectangle 0 0 $pxmin $height -fill $colour -outline $colour -tag mask $w create rectangle 0 0 $width $pymin -fill $colour -outline $colour -tag mask $w create rectangle 0 $pymax $width $height -fill $colour -outline $colour -tag mask $w lower mask } # DrawData -- # Draw the x-axis # Arguments: # w Name of the canvas # series Data series # xcrd Next x coordinate # ycrd Next y coordinate # Result: # None # Side effects: # Axis drawn in canvas # proc ::Slipchart::DrawData { w series xcrd ycrd } { variable data_series variable scaling if { $xcrd > $scaling($w,xmax) } { set xdelt $scaling($w,xdelt) set xmin $scaling($w,xmin) set xmax $scaling($w,xmax) set xminorg $xmin while { $xmax < $xcrd } { set xmin [expr {$xmin+$xdelt}] set xmax [expr {$xmax+$xdelt}] } set ymin $scaling($w,ymin) set ymax $scaling($w,ymax) worldCoordinates $w $xmin $xmax $ymin $ymax DrawXaxis $w $xmin $xmax $xdelt foreach {pxminorg pyminorg} [coordsToPixel $w $xminorg $ymin] {break} foreach {pxmin pymin} [coordsToPixel $w $xmin $ymin] {break} $w move data [expr {$pxminorg-$pxmin+1}] 0 } # # Draw the line piece # if { [info exists data_series($w,$series,x)] } { set xold $data_series($w,$series,x) set yold $data_series($w,$series,y) foreach {pxold pyold} [coordsToPixel $w $xold $yold] {break} foreach {pxcrd pycrd} [coordsToPixel $w $xcrd $ycrd] {break} $w create line $pxold $pyold $pxcrd $pycrd \ -fill black -tag data $w lower data } set data_series($w,$series,x) $xcrd set data_series($w,$series,y) $ycrd } # # Main code # canvas .c -background white -width 400 -height 200 pack .c -fill both set s [::Slipchart::createSlipchart .c {0.0 100.0 10.0} {0.0 100.0 20.0}] proc gendata {slipchart xold xd yold yd} { set xnew [expr {$xold+$xd}] set ynew [expr {$yold+(rand()-0.5)*$yd}] set ynew2 [expr {$yold+(rand()-0.5)*2.0*$yd}] $slipchart series1 $xnew $ynew $slipchart series2 $xnew $ynew2 after 500 [list gendata $slipchart $xnew $xd $ynew $yd] } after 100 [list gendata $s 0.0 15.0 50.0 30.0]