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

Last change on this file since 4775 was 3330, checked in by gah, 11 years ago

merge (by hand) with Rappture1.2 branch

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