source: trunk/gui/scripts/histogram.tcl @ 2505

Last change on this file since 2505 was 2388, checked in by gah, 13 years ago
File size: 14.3 KB
Line 
1 
2# ----------------------------------------------------------------------
3#  COMPONENT: histogram - extracts data from an XML description of a field
4#
5#  This object represents a histogram of data in an XML description of
6#  simulator output.  A histogram is similar to a field, but a field is
7#  a quantity versus position in device.  A histogram is any quantity
8#  versus any other quantity.  This class simplifies the process of
9#  extracting data vectors that represent the histogram.
10# ======================================================================
11#  AUTHOR:  Michael McLennan, Purdue University
12#  Copyright (c) 2004-2005  Purdue Research Foundation
13#
14#  See the file "license.terms" for information on usage and
15#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16# ======================================================================
17package require Itcl
18package require BLT
19
20namespace eval Rappture { # forward declaration }
21
22itcl::class Rappture::Histogram {
23    constructor {xmlobj path} { # defined below }
24    destructor { # defined below }
25
26    public method components {{pattern *}}
27    public method mesh { component }
28    public method values { component }
29    public method widths { component }
30    public method xlabels { component }
31    public method limits {which}
32    public method xmarkers {}
33    public method ymarkers {}
34    public method hints {{key ""}}
35
36    protected method Build {}
37    private method Clear { {comp ""} }
38    private method ParseData { comp }
39
40    private variable _xmlobj ""  ;# ref to lib obj with histogram data
41    private variable _hist ""    ;# lib obj representing this histogram
42    private variable _widths     ;# array of vectors of bin widths
43    private variable _yvalues    ;# array of vectors of bin heights along
44                                 ;# y-axis.
45    private variable _xvalues    ;# array of vectors of bin locations along
46                                 ;# x-axis.
47    private variable _xlabels    ;# array of labels
48    private variable _hints      ;# cache of hints stored in XML
49    private variable _xmarkers "";# list of {x,label,options} triplets.
50    private variable _ymarkers "";# list of {y,label,options} triplets.
51    private common _counter 0    ;# counter for unique vector names
52    private variable _comp2hist  ;# maps component name => x,y,w,l vectors
53}
54
55# ----------------------------------------------------------------------
56# CONSTRUCTOR
57# ----------------------------------------------------------------------
58itcl::body Rappture::Histogram::constructor {xmlobj path} {
59    if {![Rappture::library isvalid $xmlobj]} {
60        error "bad value \"$xmlobj\": should be LibraryObj"
61    }
62    set _xmlobj $xmlobj
63    set _hist [$xmlobj element -as object $path]
64
65    # build up vectors for various components of the histogram
66    Build
67}
68
69# ----------------------------------------------------------------------
70# DESTRUCTOR
71# ----------------------------------------------------------------------
72itcl::body Rappture::Histogram::destructor {} {
73    # don't destroy the _xmlobj! we don't own it!
74    itcl::delete object $_hist
75    Clear
76}
77
78# ----------------------------------------------------------------------
79# USAGE: mesh
80#
81# Returns the vector for the histogram bin locations along the
82# x-axis.
83# ----------------------------------------------------------------------
84itcl::body Rappture::Histogram::mesh { comp } {
85    if { [info exists _xvalues($comp)] } {
86        return $_xvalues($comp)
87    }
88    return ""
89}
90
91# ----------------------------------------------------------------------
92# USAGE: heights
93#
94# Returns the vector for the histogram bin heights along the y-axis.
95# ----------------------------------------------------------------------
96itcl::body Rappture::Histogram::values { comp } {
97    if { [info exists _yvalues($comp)] } {
98        return $_yvalues($comp)
99    }
100    return ""
101}
102
103# ----------------------------------------------------------------------
104# USAGE: widths
105#
106# Returns the vector for the specified histogram component <name>.
107# If the name is not specified, then it returns the vectors for the
108# overall histogram (sum of all components).
109# ----------------------------------------------------------------------
110itcl::body Rappture::Histogram::widths { comp } {
111    if { [info exists _widths($comp)] } {
112        return $_widths($comp)
113    }
114    return ""
115}
116
117# ----------------------------------------------------------------------
118# USAGE: xlabels
119#
120# Returns the vector for the specified histogram component <name>.
121# If the name is not specified, then it returns the vectors for the
122# overall histogram (sum of all components).
123# ----------------------------------------------------------------------
124itcl::body Rappture::Histogram::xlabels { comp } {
125    if { [info exists _xlabels($comp)] } {
126        return $_xlabels($comp)
127    }
128    return ""
129}
130
131# ----------------------------------------------------------------------
132# USAGE: xmarkers
133#
134# Returns the list of settings for each marker on the x-axis.
135# If no markers have been specified the empty string is returned.
136# ----------------------------------------------------------------------
137itcl::body Rappture::Histogram::xmarkers {} {
138    return $_xmarkers;
139}
140
141# ----------------------------------------------------------------------
142# USAGE: components ?<pattern>?
143#
144# Returns a list of names for the various components of this curve.
145# If the optional glob-style <pattern> is specified, then it returns
146# only the component names matching the pattern.
147# ----------------------------------------------------------------------
148itcl::body Rappture::Histogram::components {{pattern *}} {
149    set rlist ""
150    foreach name [array names _comp2hist] {
151        if {[string match $pattern $name]} {
152            lappend rlist $name
153        }
154    }
155    return $rlist
156}
157
158# ----------------------------------------------------------------------
159# USAGE: ymarkers
160#
161# Returns the list of settings for each marker on the y-axis.
162# If no markers have been specified the empty string is returned.
163# ----------------------------------------------------------------------
164itcl::body Rappture::Histogram::ymarkers {} {
165    return $_ymarkers;
166}
167
168# ----------------------------------------------------------------------
169# USAGE: limits x|xlin|xlog|y|ylin|ylog
170#
171# Returns the {min max} limits for the specified axis.
172#
173# What does it mean to view a distribution (the bins) as log scale?
174#
175# ----------------------------------------------------------------------
176itcl::body Rappture::Histogram::limits {which} {
177    set min ""
178    set max ""
179    switch -- $which {
180        x - xlin { set pos 0; set log 0; set axis xaxis }
181        xlog { set pos 0; set log 1; set axis xaxis }
182        y - ylin - v - vlin { set pos 1; set log 0; set axis yaxis }
183        ylog - vlog { set pos 1; set log 1; set axis yaxis }
184        default {
185            error "bad option \"$which\": should be x, xlin, xlog, y, ylin, ylog, v, vlin, vlog"
186        }
187    }
188
189    blt::vector create tmp
190    blt::vector create zero
191    foreach comp [array names _comphist] {
192        set vname [lindex $_comp2hist($comp) $pos]
193        $vname variable vec
194
195        if {$log} {
196            # on a log scale, use abs value and ignore 0's
197            $vname dup tmp
198            $vname dup zero
199            zero expr {tmp == 0}            ;# find the 0's
200            tmp expr {abs(tmp)}             ;# get the abs value
201            tmp expr {tmp + zero*max(tmp)}  ;# replace 0's with abs max
202            set vmin [blt::vector expr min(tmp)]
203            set vmax [blt::vector expr max(tmp)]
204        } else {
205            set vmin $vec(min)
206            set vmax $vec(max)
207        }
208
209        if {"" == $min} {
210            set min $vmin
211        } elseif {$vmin < $min} {
212            set min $vmin
213        }
214        if {"" == $max} {
215            set max $vmax
216        } elseif {$vmax > $max} {
217            set max $vmax
218        }
219    }
220    blt::vector destroy tmp zero
221
222    set val [$_hist get $axis.min]
223    if {"" != $val && "" != $min} {
224        if {$val > $min} {
225            # tool specified this min -- don't go any lower
226            set min $val
227        }
228    }
229
230    set val [$_hist get $axis.max]
231    if {"" != $val && "" != $max} {
232        if {$val < $max} {
233            # tool specified this max -- don't go any higher
234            set max $val
235        }
236    }
237    return [list $min $max]
238}
239
240# ----------------------------------------------------------------------
241# USAGE: hints ?<keyword>?
242#
243# Returns a list of key/value pairs for various hints about plotting
244# this histogram.  If a particular <keyword> is specified, then it returns
245# the hint for that <keyword>, if it exists.
246# ----------------------------------------------------------------------
247itcl::body Rappture::Histogram::hints {{keyword ""}} {
248    if {![info exists _hints]} {
249        foreach {key path} {
250            group   about.group
251            label   about.label
252            color   about.color
253            style   about.style
254            type    about.type
255            xlabel  xaxis.label
256            xdesc   xaxis.description
257            xunits  xaxis.units
258            xscale  xaxis.scale
259            xmin    xaxis.min
260            xmax    xaxis.max
261            ylabel  yaxis.label
262            ydesc   yaxis.description
263            yunits  yaxis.units
264            yscale  yaxis.scale
265            ymin    yaxis.min
266            ymax    yaxis.max
267        } {
268            set str [$_hist get $path]
269            if {"" != $str} {
270                set _hints($key) $str
271            }
272        }
273
274        if {[info exists _hints(xlabel)] && "" != $_hints(xlabel)
275              && [info exists _hints(xunits)] && "" != $_hints(xunits)} {
276            set _hints(xlabel) "$_hints(xlabel) ($_hints(xunits))"
277        }
278        if {[info exists _hints(ylabel)] && "" != $_hints(ylabel)
279              && [info exists _hints(yunits)] && "" != $_hints(yunits)} {
280            set _hints(ylabel) "$_hints(ylabel) ($_hints(yunits))"
281        }
282
283        if {[info exists _hints(group)] && [info exists _hints(label)]} {
284            # pop-up help for each histogram
285            set _hints(tooltip) $_hints(label)
286        }
287    }
288
289    if {$keyword != ""} {
290        if {[info exists _hints($keyword)]} {
291            return $_hints($keyword)
292        }
293        return ""
294    }
295    return [array get _hints]
296}
297
298# ----------------------------------------------------------------------
299# USAGE: Build
300#
301# Used internally to build up the vector representation for the
302# histogram when the object is first constructed, or whenever the histogram
303# data changes.  Discards any existing vectors and builds everything
304# from scratch.
305# ----------------------------------------------------------------------
306itcl::body Rappture::Histogram::Build {} {
307    # discard any existing data
308    Clear
309    #
310    # Scan through the components of the histogram and create
311    # vectors for each part.  Right now there's only one
312    # component.  I left in the component tag in case future
313    # enhancements require more than one component.
314    #
315    foreach cname [$_hist children -type component] {
316        ParseData $cname
317    }
318    # Creates lists of x and y marker data.
319    set _xmarkers {}
320    set _ymarkers {}
321    foreach cname [$_hist children -type "marker" xaxis] {
322        set at     [$_hist get "xaxis.$cname.at"]
323        set label  [$_hist get "xaxis.$cname.label"]
324        set styles [$_hist get "xaxis.$cname.style"]
325        set data [list $at $label $styles]
326        lappend _xmarkers $data
327    }
328    foreach cname [$_hist children -type "marker" yaxis] {
329        set at     [$_hist get "yaxis.$cname.at"]
330        set label  [$_hist get "yaxis.$cname.label"]
331        set styles [$_hist get "yaxis.$cname.style"]
332        set data [list $at $label $styles]
333        lappend _xmarkers $data
334    }
335}
336
337#
338# ParseData --
339#
340#       Parse the components data representations.  The following
341#       elements may be used <xy>, <xhw>, <namevalue>, <xvector>,
342#       <yvector>.  Only one element is used for data. 
343#
344itcl::body Rappture::Histogram::ParseData { comp } {
345    # Create new vectors or discard any existing data
346    set _xvalues($comp) [blt::vector create \#auto]
347    set _yvalues($comp) [blt::vector create \#auto]
348    set _widths($comp) [blt::vector create \#auto]
349    set _xlabels($comp) {}
350
351    set xydata [$_hist get ${comp}.xy]
352    if { $xydata != "" } {
353        set count 0
354        foreach line [split $xydata \n] {
355            foreach {name value} $line break
356            $_yvalues($comp) append $value
357            $_xvalues($comp) append $count
358            lappend _xlabels($comp) $name
359            incr count
360        }           
361        set _comp2hist($comp) [list $_xvalues($comp) $_yvalues($comp)]
362        return
363    }
364    set xhwdata [$_hist get ${comp}.xhw]
365    if { $xhwdata != "" } {
366        set count 0
367        foreach line [split $xhwdata \n] {
368            set n [scan $line {%s %s %s} name h w]
369            lappend _xlabels($comp) $name
370            $_xvalues($comp) append $count
371            $_yvalues($comp) append $h
372            if { $n == 3 } {
373                $_widths($comp) append $w
374            }
375            incr count
376        }           
377        set _comp2hist($comp) [list $_xvalues($comp) $_yvalues($comp)]
378        return
379
380        # FIXME:  There must be a width specified for each bin location.
381        #         If this isn't true, we default to uniform widths
382        #         (zero-length _widths vector == uniform).
383        if { [$_xvalues($comp) length] != [$_widths($comp) length] } {
384            $_widths($comp) set {}
385        }
386        set _comp2hist($comp) [list $_xvalues($comp) $_yvalues($comp)]
387        return
388    }
389    set xv [$_hist get $comp.xvector]
390    set yv [$_hist get $comp.yvector]
391    if { $xv != "" && $yv != "" } {
392        $_yvalues($comp) set $yv
393        $_xvalues($comp) seq 0 [$yv length]
394        set _xlabels($comp)
395    }
396    set _comp2hist($comp) [list $_xvalues($comp) $_yvalues($comp)]
397}
398
399itcl::body Rappture::Histogram::Clear { {comp ""} } {
400    if { $comp == "" } {
401        foreach name [array names _widths] {
402            blt::vector destroy $_widths($name)
403        }
404        array unset _widths
405        foreach name [array names _yvalues] {
406            blt::vector destroy $_yvalues($name)
407        }
408        array unset _yvalues
409        foreach name [array names _xvalues] {
410            blt::vector destroy $_xvalues($name)
411        }
412        array unset _xvalues
413        array unset _xlabels
414        array unset _comp2hist
415        return
416    }
417    if { [info exists _widths($comp)] } {
418        blt::vector destroy $_widths($comp)
419    }
420    if { [info exists _yvalues($comp)] } {
421        blt::vector destroy $_yvalues($comp)
422    }
423    if { [info exists _xvalues($comp)] } {
424        blt::vector destroy $_xvalues($comp)
425    }
426    array unset _xvalues $comp
427    array unset _yvalues $comp
428    array unset _widths $comp
429    array unset _xlabels $comp
430    array unset _comp2hist $comp
431}
432
Note: See TracBrowser for help on using the repository browser.