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

Last change on this file since 5996 was 5659, checked in by ldelgass, 9 years ago

whitespace

File size: 14.3 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
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-2012  HUBzero Foundation, LLC
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            xorient xaxis.orientation
259            xscale  xaxis.scale
260            xmin    xaxis.min
261            xmax    xaxis.max
262            ylabel  yaxis.label
263            ydesc   yaxis.description
264            yunits  yaxis.units
265            yscale  yaxis.scale
266            ymin    yaxis.min
267            ymax    yaxis.max
268        } {
269            set str [$_hist get $path]
270            if {"" != $str} {
271                set _hints($key) $str
272            }
273        }
274
275        if {[info exists _hints(xlabel)] && "" != $_hints(xlabel)
276              && [info exists _hints(xunits)] && "" != $_hints(xunits)} {
277            set _hints(xlabel) "$_hints(xlabel) ($_hints(xunits))"
278        }
279        if {[info exists _hints(ylabel)] && "" != $_hints(ylabel)
280              && [info exists _hints(yunits)] && "" != $_hints(yunits)} {
281            set _hints(ylabel) "$_hints(ylabel) ($_hints(yunits))"
282        }
283
284        if {[info exists _hints(group)] && [info exists _hints(label)]} {
285            # pop-up help for each histogram
286            set _hints(tooltip) $_hints(label)
287        }
288    }
289
290    if {$keyword != ""} {
291        if {[info exists _hints($keyword)]} {
292            return $_hints($keyword)
293        }
294        return ""
295    }
296    return [array get _hints]
297}
298
299# ----------------------------------------------------------------------
300# USAGE: Build
301#
302# Used internally to build up the vector representation for the
303# histogram when the object is first constructed, or whenever the histogram
304# data changes.  Discards any existing vectors and builds everything
305# from scratch.
306# ----------------------------------------------------------------------
307itcl::body Rappture::Histogram::Build {} {
308    # discard any existing data
309    Clear
310    #
311    # Scan through the components of the histogram and create
312    # vectors for each part.  Right now there's only one
313    # component.  I left in the component tag in case future
314    # enhancements require more than one component.
315    #
316    foreach cname [$_hist children -type component] {
317        ParseData $cname
318    }
319    # Creates lists of x and y marker data.
320    set _xmarkers {}
321    set _ymarkers {}
322    foreach cname [$_hist children -type "marker" xaxis] {
323        set at     [$_hist get "xaxis.$cname.at"]
324        set label  [$_hist get "xaxis.$cname.label"]
325        set styles [$_hist get "xaxis.$cname.style"]
326        set data [list $at $label $styles]
327        lappend _xmarkers $data
328    }
329    foreach cname [$_hist children -type "marker" yaxis] {
330        set at     [$_hist get "yaxis.$cname.at"]
331        set label  [$_hist get "yaxis.$cname.label"]
332        set styles [$_hist get "yaxis.$cname.style"]
333        set data [list $at $label $styles]
334        lappend _xmarkers $data
335    }
336}
337
338#
339# ParseData --
340#
341#       Parse the components data representations.  The following
342#       elements may be used <xy>, <xhw>, <namevalue>, <xvector>,
343#       <yvector>.  Only one element is used for data.
344#
345itcl::body Rappture::Histogram::ParseData { comp } {
346    # Create new vectors or discard any existing data
347    set _xvalues($comp) [blt::vector create \#auto]
348    set _yvalues($comp) [blt::vector create \#auto]
349    set _widths($comp) [blt::vector create \#auto]
350    set _xlabels($comp) {}
351
352    set xydata [$_hist get ${comp}.xy]
353    if { $xydata != "" } {
354        set count 0
355        foreach {name value} [regsub -all "\[ \t\n]+" $xydata { }] {
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 {name h w} [regsub -all "\[ \t\n]+" $xhwdata { }] {
368            lappend _xlabels($comp) $name
369            $_xvalues($comp) append $count
370            $_yvalues($comp) append $h
371            $_widths($comp) append $w
372            incr count
373        }
374        set _comp2hist($comp) [list $_xvalues($comp) $_yvalues($comp)]
375        return
376    }
377
378    # If we reached here, must be <yvector>
379    $_yvalues($comp) set [$_hist get ${comp}.yvector]
380    $_xvalues($comp) length [$_yvalues($comp) length]
381    $_xvalues($comp) seq 1 [$_yvalues($comp) length]
382    set _xlabels($comp) [$_hist get ${comp}.xvector]
383    set _comp2hist($comp) [list $_xvalues($comp) $_yvalues($comp)]
384}
385
386itcl::body Rappture::Histogram::Clear { {comp ""} } {
387    if { $comp == "" } {
388        foreach name [array names _widths] {
389            blt::vector destroy $_widths($name)
390        }
391        array unset _widths
392        foreach name [array names _yvalues] {
393            blt::vector destroy $_yvalues($name)
394        }
395        array unset _yvalues
396        foreach name [array names _xvalues] {
397            blt::vector destroy $_xvalues($name)
398        }
399        array unset _xvalues
400        array unset _xlabels
401        array unset _comp2hist
402        return
403    }
404    if { [info exists _widths($comp)] } {
405        blt::vector destroy $_widths($comp)
406    }
407    if { [info exists _yvalues($comp)] } {
408        blt::vector destroy $_yvalues($comp)
409    }
410    if { [info exists _xvalues($comp)] } {
411        blt::vector destroy $_xvalues($comp)
412    }
413    array unset _xvalues $comp
414    array unset _yvalues $comp
415    array unset _widths $comp
416    array unset _xlabels $comp
417    array unset _comp2hist $comp
418}
419
Note: See TracBrowser for help on using the repository browser.