1# plotstat.tcl --
2#
3#    Set of very simple drawing routines, belonging to the statistics
4#    package
5#
6# version 0.1: initial implementation, january 2003
7
8namespace eval ::math::statistics {}
9
10# plot-scale
11#    Set the scale for a plot in the given canvas
12#
13# Arguments:
14#    canvas   Canvas widget to use
15#    xmin     Minimum x value
16#    xmax     Maximum x value
17#    ymin     Minimum y value
18#    ymax     Maximum y value
19#
20# Result:
21#    None
22#
23# Side effect:
24#    Array elements set
25#
26proc ::math::statistics::plot-scale { canvas xmin xmax ymin ymax } {
27    variable plot
28
29    if { $xmin == $xmax } { set xmax [expr {1.1*$xmin+1.0}] }
30    if { $ymin == $ymax } { set ymax [expr {1.1*$ymin+1.0}] }
31
32    set plot($canvas,xmin) $xmin
33    set plot($canvas,xmax) $xmax
34    set plot($canvas,ymin) $ymin
35    set plot($canvas,ymax) $ymax
36
37    set cwidth  [$canvas cget -width]
38    set cheight [$canvas cget -height]
39    set cx      20
40    set cy      20
41    set cx2     [expr {$cwidth-$cx}]
42    set cy2     [expr {$cheight-$cy}]
43
44    set plot($canvas,cx)   $cx
45    set plot($canvas,cy)   $cy
46
47    set plot($canvas,dx)   [expr {($cwidth-2*$cx)/double($xmax-$xmin)}]
48    set plot($canvas,dy)   [expr {($cheight-2*$cy)/double($ymax-$ymin)}]
49    set plot($canvas,cx2)  $cx2
50    set plot($canvas,cy2)  $cy2
51
52    $canvas create line $cx $cy $cx $cy2 $cx2 $cy2 -tag axes
53}
54
55# plot-xydata
56#    Create a simple XY plot in the given canvas (collection of dots)
57#
58# Arguments:
59#    canvas   Canvas widget to use
60#    xdata    Series of independent data
61#    ydata    Series of dependent data
62#    tag      Tag to give to the plotted data (defaults to xyplot)
63#
64# Result:
65#    None
66#
67# Side effect:
68#    Simple xy graph in the canvas
69#
70# Note:
71#    The tag can be used to manipulate the xy graph
72#
73proc ::math::statistics::plot-xydata { canvas xdata ydata {tag xyplot} } {
74    PlotXY $canvas points $tag $xdata $ydata
75}
76
77# plot-xyline
78#    Create a simple XY plot in the given canvas (continuous line)
79#
80# Arguments:
81#    canvas   Canvas widget to use
82#    xdata    Series of independent data
83#    ydata    Series of dependent data
84#    tag      Tag to give to the plotted data (defaults to xyplot)
85#
86# Result:
87#    None
88#
89# Side effect:
90#    Simple xy graph in the canvas
91#
92# Note:
93#    The tag can be used to manipulate the xy graph
94#
95proc ::math::statistics::plot-xyline { canvas xdata ydata {tag xyplot} } {
96    PlotXY $canvas line $tag $xdata $ydata
97}
98
99# plot-tdata
100#    Create a simple XY plot in the given canvas (the index in the list
101#    is the horizontal coordinate; points)
102#
103# Arguments:
104#    canvas   Canvas widget to use
105#    tdata    Series of dependent data
106#    tag      Tag to give to the plotted data (defaults to xyplot)
107#
108# Result:
109#    None
110#
111# Side effect:
112#    Simple xy graph in the canvas
113#
114# Note:
115#    The tag can be used to manipulate the xy graph
116#
117proc ::math::statistics::plot-tdata { canvas tdata {tag xyplot} } {
118    PlotXY $canvas points $tag {} $tdata
119}
120
121# plot-tline
122#    Create a simple XY plot in the given canvas (the index in the list
123#    is the horizontal coordinate; line)
124#
125# Arguments:
126#    canvas   Canvas widget to use
127#    tdata    Series of dependent data
128#    tag      Tag to give to the plotted data (defaults to xyplot)
129#
130# Result:
131#    None
132#
133# Side effect:
134#    Simple xy graph in the canvas
135#
136# Note:
137#    The tag can be used to manipulate the xy graph
138#
139proc ::math::statistics::plot-tline { canvas tdata {tag xyplot} } {
140    PlotXY $canvas line $tag {} $tdata
141}
142
143# PlotXY
144#    Create a simple XY plot (points or lines) in the given canvas
145#
146# Arguments:
147#    canvas   Canvas widget to use
148#    type     Type: points or line
149#    tag      Tag to give to the plotted data
150#    xdata    Series of independent data (if empty: index used instead)
151#    ydata    Series of dependent data
152#
153# Result:
154#    None
155#
156# Side effect:
157#    Simple xy graph in the canvas
158#
159# Note:
160#    This is the actual routine
161#
162proc ::math::statistics::PlotXY { canvas type tag xdata ydata } {
163    variable plot
164
165    if { ![info exists plot($canvas,xmin)] } {
166	return -code error -errorcode "No scaling given for canvas $canvas"
167    }
168
169    set xmin $plot($canvas,xmin)
170    set xmax $plot($canvas,xmax)
171    set ymin $plot($canvas,ymin)
172    set ymax $plot($canvas,ymax)
173    set dx   $plot($canvas,dx)
174    set dy   $plot($canvas,dy)
175    set cx   $plot($canvas,cx)
176    set cy   $plot($canvas,cy)
177    set cx2  $plot($canvas,cx2)
178    set cy2  $plot($canvas,cy2)
179
180    set plotpoints [expr {$type == "points"}]
181    set xpresent   [expr {[llength $xdata] > 0}]
182    set idx        0
183    set coords     {}
184
185    foreach y $ydata {
186	if { $xpresent } {
187	    set x [lindex $xdata $idx]
188	} else {
189	    set x $idx
190	}
191	incr idx
192
193	if { $x == {}    } continue
194	if { $y == {}    } continue
195	if { $x >  $xmax } continue
196	if { $x <  $xmin } continue
197	if { $y >  $ymax } continue
198	if { $y <  $ymin } continue
199
200	if { $plotpoints } {
201	    set xc [expr {$cx+$dx*($x-$xmin)-2}]
202	    set yc [expr {$cy2-$dy*($y-$ymin)-2}]
203	    set xc2 [expr {$xc+4}]
204	    set yc2 [expr {$yc+4}]
205	    $canvas create oval $xc $yc $xc2 $yc2 -tag $tag -fill black
206	} else {
207	    set xc [expr {$cx+$dx*($x-$xmin)}]
208	    set yc [expr {$cy2-$dy*($y-$ymin)}]
209	    lappend coords $xc $yc
210	}
211    }
212
213    if { ! $plotpoints } {
214	$canvas create line $coords -tag $tag
215    }
216}
217
218# plot-histogram
219#    Create a simple histogram in the given canvas
220#
221# Arguments:
222#    canvas   Canvas widget to use
223#    counts   Series of bucket counts
224#    limits   Series of upper limits for the buckets
225#    tag      Tag to give to the plotted data (defaults to xyplot)
226#
227# Result:
228#    None
229#
230# Side effect:
231#    Simple histogram in the canvas
232#
233# Note:
234#    The number of limits determines how many bars are drawn,
235#    the number of counts that is expected is one larger. The
236#    lower and upper limits of the first and last bucket are
237#    taken to be equal to the scale's extremes
238#
239proc ::math::statistics::plot-histogram { canvas counts limits {tag xyplot} } {
240    variable plot
241
242    if { ![info exists plot($canvas,xmin)] } {
243	return -code error -errorcode DATA "No scaling given for canvas $canvas"
244    }
245
246    if { ([llength $counts]-[llength $limits]) != 1 } {
247	return -code error -errorcode ARG \
248		"Number of counts does not correspond to number of limits"
249    }
250
251    set xmin $plot($canvas,xmin)
252    set xmax $plot($canvas,xmax)
253    set ymin $plot($canvas,ymin)
254    set ymax $plot($canvas,ymax)
255    set dx   $plot($canvas,dx)
256    set dy   $plot($canvas,dy)
257    set cx   $plot($canvas,cx)
258    set cy   $plot($canvas,cy)
259    set cx2  $plot($canvas,cx2)
260    set cy2  $plot($canvas,cy2)
261
262    #
263    # Construct a sufficiently long list of x-coordinates
264    #
265    set xdata [concat $xmin $limits $xmax]
266
267    set idx   0
268    foreach x $xdata y $counts {
269	incr idx
270
271	if { $y == {}    } continue
272
273	set x1 $x
274	if { $x <  $xmin } { set x1 $xmin }
275	if { $x >  $xmax } { set x1 $xmax }
276
277	if { $y >  $ymax } { set y $ymax }
278	if { $y <  $ymin } { set y $ymin }
279
280	set x2  [lindex $xdata $idx]
281	if { $x2 <  $xmin } { set x2 $xmin }
282	if { $x2 >  $xmax } { set x2 $xmax }
283
284	set xc  [expr {$cx+$dx*($x1-$xmin)}]
285	set xc2 [expr {$cx+$dx*($x2-$xmin)}]
286	set yc  [expr {$cy2-$dy*($y-$ymin)}]
287	set yc2 $cy2
288
289	$canvas create rectangle $xc $yc $xc2 $yc2 -tag $tag -fill blue
290    }
291}
292
293#
294# Simple test code
295#
296if { [info exists ::argv0] && ([file tail [info script]] == [file tail $::argv0]) } {
297
298    set xdata {1 2 3 4 5 10 20 6 7 8 1 3 4 5 6 7}
299    set ydata {2 3 4 5 6 10 20 7 8 1 3 4 5 6 7 1}
300
301    canvas .c
302    canvas .c2
303    pack   .c .c2 -side top -fill both
304    ::math::statistics::plot-scale .c  0 10 0 10
305    ::math::statistics::plot-scale .c2 0 20 0 10
306
307    ::math::statistics::plot-xydata .c  $xdata $ydata
308    ::math::statistics::plot-xyline .c  $xdata $ydata
309    ::math::statistics::plot-histogram .c2 {1 3 2 0.1 4 2} {-1 3 10 11 23}
310    ::math::statistics::plot-tdata  .c2 $xdata
311    ::math::statistics::plot-tline  .c2 $xdata
312}
313