summaryrefslogtreecommitdiff
path: root/ipl/gprocs/barchart.icn
blob: 5522ebb08af6ffc1b609c4aa818fecd66a6f379a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
############################################################################
#
#	File:     barchart.icn
#
#	Subject:  Procedures for dynamically growing barchart
#
#	Author:   Gregg M. Townsend
#
#	Date:     August 14, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#	These procedures draw barcharts that can grow dynamically.
#
#	barchart(win, x, y, dx, dy, sf, n, l, w, b) creates a barchart.
#
#	setbar(bc, n, v)	sets the value of a bar.
#
#	rebar(bc, sf)		redraws a barchart with a new scaling factor.
#
############################################################################
#
#   barchart(win, x, y, dx, dy, sf, n, l, w, b) -- establish a barchart
#
#	win      window
#	x,y      position of base of first bar
#	dx,dy    distance to base of second bar (either dx or dy should be
#		  zero)
#	sf       scaling (pixels per unit of value, + or -, need not be
#		  integer)
#	n        number of bars
#	l,w      length (maximum) and width of one bar
#	b        logarithmic base, if bars are to be scaled logarithmically
#
#	barchart() establishes structures for building a barchart.  Any of the
#	eight possible orthogonal orientations can be selected depending on the
#	signs of dx, dy, and sf.
#
#	The absolute value of sf establishes a linear scaling from barchart
#	values to number of pixels.  Scaling is handled such that a value of 1
#	makes the first mark on a bar and then each increment of sf lengthens
#	the bar by one pixel.  If a bar would exceed the limit then the entire
#	chart is rescaled so that only half the range is then used.
#
#   setbar(bc, n, v) - set bar n of barchart bc to represent value v
#
#	It is assumed that v>0 and that bars never shrink; but they may grow.
#
#   rebar(bc, sf) - redraw barchart with new scaling factor sf.
#
#	sf is assumed to be of the same sign as the previous scaling factor.
#
#   Example:
#
#	Suppose "scores" is a list of scores ranging from 0 to 100.
#	This code fragment dynamically draws a histogram using 21 bins.
#
#	The call to barchart() specifies:
#	    The lower left-hand corner of the barchart is (10, 190).
#	    The next bar is 10 pixels to its right, which would be (20, 190).
#	    The bars grow upward, to smaller y values, so the scaling factor
#	    is negative; each score will grow its bar by 5 pixels.
#	    Each bar grows to a maximum length of 180 pixels; the width is 8.
#	    No base is given, so scaling is linear.
#
#	bc := barchart(win, 10, 190, 10, 0, -5, 21, 180, 8)
#	b := list(21, 0)		# histogram bins
#	every n := !scores do {
#	    i := n / 5			# bin (and bar) number
#	    b[i] +:= 1			# increment bin count
#	    setbar(bc, i, b[i])		# update display
#	    }
#
############################################################################
#
#  Requires:  Version 9 graphics
#
############################################################################

record BC_rec(win, x, y, dx, dy, sf, n, l, w, b, len, val, round)

procedure barchart(win, x, y, dx, dy, sf, n, l, w, b)	#: draw barchart
   local bc
   bc := BC_rec(win, x, y, dx, dy, sf, n, l, w, b)
   bc.len := list(n, 0)
   bc.val := list(n)
   if sf > 0 then
      bc.round := 0.99999
   else
      bc.round := -0.99999
   rebar(bc, sf)				# clear area
   return bc
end


## setbar(bc, n, v) - set bar n of barchart bc to represent value v
#
#  It is assumed that v>0 and that bars never shrink; but they may grow.

procedure setbar(bc, n, v)			#: set bar value on barchart
   local x, y, o, oldlen, newlen, incr

   v := log(v, \bc.b)
   oldlen := bc.len[n] | fail
   newlen := integer(v * bc.sf + bc.round)

   if abs(newlen) > bc.l then {
      # need to rescale first
      rebar(bc, 0.5 * bc.sf * real(bc.l) / real(abs(newlen-1)))
      return setbar(bc, n, v)
      }

   # lengthen the bar
   if (incr := newlen - oldlen) ~= 0 then {
      if bc.dx ~= 0 then {

         # horizontal baseline
         x := bc.x + (n - 1) * bc.dx
         y := bc.y + oldlen
         if incr < 0 then
            FillRectangle(bc.win, x, y + incr, bc.w, -incr)
         else
            FillRectangle(bc.win, x, y, bc.w, incr)
         }

      else {

         # vertical baseline
         x := bc.x + oldlen
         y := bc.y + (n - 1) * bc.dy
         if incr < 0 then
            FillRectangle(bc.win, x + incr, y, -incr, bc.w)
         else
            FillRectangle(bc.win, x, y, incr, bc.w)
         }
      bc.len[n] := newlen
      bc.val[n] := v
      }
   return
end


## rebar(bc, sf) - redraw barchart with new scaling factor sf.
#
#  sf is assumed to be of the same sign as the previous scaling factor.

procedure rebar(bc, sf)				#: redraw barchart
   local i, l, x, y, dx, dy

   if bc.sf > 0 then
      l := bc.l
   else
      l := -bc.l
   x := bc.x
   y := bc.y

   if bc.dx ~= 0 then {
      dx := bc.n * bc.dx
      dy := l
      }
   else {
      dx := l
      dy := bc.n * bc.dy
      }

   # force all values positive (negative is wrong, but works under OpenWindows!)
   if dx < 0 then {
      x +:= dx
      dx := -dx
      }
   if dy < 0 then {
      y +:= dy
      dy := -dy
      }
   EraseArea(bc.win, x, y, dx, dy)

   bc.len := list(bc.n, 0)
   bc.sf := sf
   every i := 1 to *bc.len do
      setbar(bc, i, \bc.val[i])
   return
end


#  ## test program
#  #
#  #  usage:  barchart [dx [dy [sf]]]
#  #
#  #  background is deliberately different in order to see what gets cleared
#
#  procedure main(args)
#     local dx, dy, sf, win, n, l, bc, i
#     dx := args[1] | 5
#     dy := args[2] | 0
#     sf := args[3] | -1
#     win := open("bars", "g", "width=500", "height=500")
#     l := list(50, 0)
#     bc := barchart(win, 250, 250, dx, dy, sf, *l, 200, 4)
#     Fg(win, "papayawhip")
#     FillRectangle(win, 0, 0, 500, 500)
#     Fg(win, "black")
#     every 1 to 5000 do {
#        i := ?5 + ?5 + integer(10 * log(1+20*?0))	# nonuniform random bar
#        setbar(bc, i, l[i] +:= 1)
#        flush(win)
#        }
#     while not upto('qQ', reads(win))
#     end