Version 22 of A Graph plotter

Updated 2013-04-25 15:57:58 by pooryorick

Summary

GWM:

Based on A Little graph Plotter.

See Also

Fun with Functions
Another Graphing Widget

Description

2006-05: corrected for missing proc.

Modified to draw a set of points or multiple sets of points on a canvas. The proc plot takes a list of data to be plotted and a set of titles (one per graph). The data format is: [list of xposition [list of ypositions (1,2 or more)]]] eg:

PYK 2013-04-25: Made axis labels more readable. Moved legend to lower right

set res [[0.0 {0.0 1.0} 0.333 {0.327  0.944 } 0.6667 {0.618 0.785}]]

will define 3 points at x = 0, .333, .6667 with 2 y coordinates per x position (2 graphs). Better example data at the end of this page.

#! /bin/env tclsh

package require Tk
package require math

proc graphscale {v vmin vmax pixels} {
    return [expr {$pixels*($v-$vmin)/($vmax-$vmin)}]
}

proc plot {res titles} {
    # adapted the simple graph plotter, http://wiki.tcl.tk/8552
    # --------------------------
    #   params
    # --------------------------
    # title
    # canvas width & height
    array set params {
        title     {Graph}
        width     400
        height    400
        colors    {ff 00 00}
    }

    # canvas
    if [winfo exists .c] { ;# delete it. Same as clear.
        destroy .c
    }
    canvas .c -width $params(width) -height $params(height) \
        -xscrollincrement 1 -bg beige
    pack .c

    wm title . $params(title)

    # get range of x & y
    set xmin 100000000
    set xmax -100000000
    set ymin 100000000
    set ymax -100000000
    set vv0 [lindex $res 1]
    set nvars [llength $vv0]
    foreach {r y} $res {
        set xmin [::math::min $xmin $r]
        set xmax [::math::max $xmax $r]
        foreach yv $y { ;# get range of Y coordinate
            set ymin [::math::min $ymin $yv]
            set ymax [::math::max $ymax $yv]
        }
    }

    #how often tick marks
    set tspace [expr ($xmax-$xmin)/8.0] ;# how often to draw grid in X
    set nexttic [expr int($xmin/$tspace)*$tspace]
    foreach {r y} $res { ;# draw grid
        if {$r >= $nexttic} {
            set xpix [graphscale $nexttic $xmin $xmax $params(width)]
            set nexttic [expr $nexttic+$tspace]
            .c create text [expr $xpix-10] 0 -anchor n -text [format %.2f $r] \
                -fill gray
            .c create line $xpix 0 $xpix $params(height) -fill gray
        }
    }

    #how often Ytick marks
    set tspace [expr ($ymax-$ymin)/8.0] ;# how often to draw grid in X
    set nexttic [expr int($ymin/$tspace)*$tspace]
    while {$nexttic < $ymax} { ;# draw grid
        set ypix [graphscale $nexttic $ymax $ymin $params(height)]
        set nexttic [expr $nexttic+$tspace]
        .c create text 20 [expr $ypix-10] -anchor n -text [format %.2f $nexttic] \
            -fill gray
        .c create line 0 $ypix $params(width) $ypix -fill gray
    }

    set lastheight $params(height)
    for {set iy 0} {$iy<$nvars} {incr iy} {
        lassign $params(colors) red green blue
        lappend params(colors) $red 
        set params(colors) [lreplace $params(colors) 0 0]

        #label graph
        set title [lindex $titles $iy] 
        .c create text -1000 0 -anchor n -text $title \
            -fill #$red$green$blue -tag $title
        lassign [.c bbox $title] textx1 texty1 textx2 texty2
        set textwidth [expr {abs($textx2-$textx1)}]
        set textheight [expr {abs($texty2-$texty1)}]
        .c coords $title [expr {$params(width)-$textwidth}] \
            [set lastheight [expr {$lastheight-$textheight}]]

        set coordlist {}
        foreach {r y} $res {
                set xpix [graphscale $r $xmin $xmax $params(width)]
                set vv [lindex $y $iy]
                set v [graphscale $vv $ymax $ymin $params(height)]
                lappend coordlist $xpix $v
           set rold $xpix
           set yold $v         ;# vector of y values
         }
        .c create line $coordlist -fill #$red$green$blue
    }
}

#Exercise the graph thus:
set plot [list] 
for {set i 0} {$i<20} {incr i} {
    set plot [linsert $plot 0 [expr $i/3.0] [expr sin($i/3.0)]]
}
plot $plot Sine

# Or 2 graphs (values of sin(x) and cos(x) are placed in a list of results at x):

set plot [list] 
for {set i 0} {$i<20} {incr i} {
    lappend plot [expr $i/3.0] [list [expr sin($i/3.0)] \
        [expr cos($i/3.0)]]
}

plot $plot {Sine Cos}

Misc

DPO: In the plot proc above, there is a call to a "scale" command. This does not match the tk usage which requires a path as the first arg. What "scale" command is this? It fails for me. (I am forced to use tcl 8.0.5 due to API limitations)

GWM: Sorry, a bit slow to respond here... supplied new function graphscale and renamed old proc scale to graphscale. This works a bit better.