source: trunk/lang/tcl/scripts/objects/curve/curve.rp @ 2154

Last change on this file since 2154 was 2154, checked in by mmc, 14 years ago

Added a definition for the "choice" object, along with the "choices" editor
for specifying values.

Numerous fixes to the object system: Cleaned up the palettes for all
objects so they can be used to warn about things like output objects on
the input side. Marked may attributes as "input only" via the new -only
option for attributes. Fixed the label/description checks so that they
occur properly when inherited from the "base" object class.

File size: 15.7 KB
Line 
1# ----------------------------------------------------------------------
2#  RAPPTURE OBJECT: curve
3#
4#  A set of (x,y) points.  Usually used as the output from a simulation,
5#  although it could also be an input if we create an input editor
6#  to specify values.
7#
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2011  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15
16object curve -extends base {
17    palettes "Outputs"
18
19    help http://rappture.org/wiki/rp_xml_ele_curve
20
21    attr group -title "Plotting Group" -type string -path about.group
22    attr xlabel -title "X-axis Label" -type string -path xaxis.label
23    attr xdesc  -title "X-axis Description" -type string -path xaxis.description
24    attr xunits -title "X-axis Units" -type units -path xaxis.units
25    attr ylabel -title "Y-axis Label" -type string -path yaxis.label
26    attr ydesc  -title "Y-axis Description" -type string -path yaxis.description
27    attr yunits -title "Y-axis Units" -type units -path yaxis.units
28
29    check xlabel {
30        if {[string length [string trim $attr(xlabel)]] == 0} {
31            return [list warning "Should set a label that describes the x-axis of this plot."]
32        }
33    }
34    check xdesc {
35        if {[string length [string trim $attr(xdesc)]] == 0} {
36            return [list warning "Should include a description of what the x-axis represents, physical meaning, expected range, etc."]
37        }
38    }
39
40    check ylabel {
41        if {[string length [string trim $attr(ylabel)]] == 0} {
42            return [list warning "Should set a label that describes the y-axis of this plot."]
43        }
44    }
45    check ydesc {
46        if {[string length [string trim $attr(ydesc)]] == 0} {
47            return [list warning "Should include a description of what the y-axis represents, physical meaning, expected range, etc."]
48        }
49    }
50
51    storage {
52        private variable _xvecs       ;# maps comp name => x-axis vector
53        private variable _yvecs       ;# maps comp name => y-axis vector
54        private variable _hints       ;# store "hints" based on attributes
55        private variable _xmarkers "" ;# list of {x,label,options} for markers
56        private variable _ymarkers "" ;# list of {y,label,options} for markers
57    }
58    clear {
59        foreach comp [array names _xvecs] {
60            blt::vector destroy $_xvecs($comp)
61            blt::vector destroy $_yvecs($comp)
62        }
63        catch {unset _xvecs}
64        catch {unset _yvecs}
65        catch {unset _hints}
66        set _xmarkers ""
67        set _ymarkers ""
68    }
69
70    # ------------------------------------------------------------------
71    # IMPORT: string
72    # ------------------------------------------------------------------
73    import string {val} {
74        set xv [blt::vector create \#auto]
75        set yv [blt::vector create \#auto]
76
77        set num 0
78        foreach line [split $val \n] {
79            if {[string index $line 0] eq "#"} {
80                # skip over lines that start with hash mark
81                continue
82            }
83
84            if {[string trim $line] eq ""} {
85                # blank line means new component
86                if {[$xv length] > 0} {
87                    # if we have something stored, save this and start another
88                    set cname "c[incr num]"
89                    set _xvecs($cname) $xv
90                    set _yvecs($cname) $yv
91                    set xv [blt::vector create \#auto]
92                    set yv [blt::vector create \#auto]
93                }
94                continue
95            }
96
97            set x [lindex $line 0]
98            if {![string is double -strict $x]} {
99                blt::vector destroy $xv $yv
100                error "bad value \"$x\": should be double number"
101            }
102
103            set y [lindex $line 1]; if {$y eq ""} { set y 0 }
104            if {![string is double -strict $y]} {
105                blt::vector destroy $xv $yv
106                error "bad value \"$y\": should be double number"
107            }
108
109            $xv append $x
110            $yv append $y
111        }
112
113        if {[$xv length] > 0} {
114            # if we have something stored, save the last component
115            set cname "c[incr num]"
116            set _xvecs($cname) $xv
117            set _yvecs($cname) $yv
118        }
119    }
120
121    # ------------------------------------------------------------------
122    # EXPORT: string
123    # ------------------------------------------------------------------
124    export string {var} {
125        upvar $var v
126        set v ""
127        set nseparators [expr {[array size _xvecs] - 1}]
128
129        foreach cname [array names _xvecs] {
130            foreach x [$_xvecs($cname) range 0 end] y [$_yvecs($cname) range 0 end] {
131                append v "$x\t$y\n"
132            }
133
134            # add blank lines between components
135            if {$nseparators > 0} {
136                append v "\n"
137                incr nseparators -1
138            }
139        }
140    }
141
142    # ------------------------------------------------------------------
143    # IMPORT: xml
144    # ------------------------------------------------------------------
145    import xml {xmlobj path} {
146        attr import $xmlobj $path
147
148        foreach cname [$xmlobj children -type component $path] {
149            set xv [blt::vector create \#auto]
150            set yv [blt::vector create \#auto]
151
152            set xydata [$xmlobj get $path.$cname.xy]
153            if {[string length $xydata] > 0} {
154                set tmp [blt::vector create \#auto]
155                $tmp set $xydata
156                $tmp split $xv $yv
157                blt::vector destroy $tmp
158            } else {
159                $xv set [$xmlobj get $path.$cname.xvector]
160                $yv set [$xmlobj get $path.$cname.yvector]
161            }
162
163            set xlen [$xv length]
164            set ylen [$yv length]
165            if {$xlen == 0 && $ylen == 0} {
166                blt::vector destroy $xv $yv
167                error "can't find any data at $path.$cname"
168            } elseif {[$xv length] != [$yv length]} {
169                blt::vector destroy $xv $yv
170                error "mismatch between x- and y-axes: $xlen x values, versus $ylen y values"
171            }
172            set _xvecs($cname) $xv
173            set _yvecs($cname) $yv
174        }
175
176        foreach elem [$xmlobj children -type "marker" $path.xaxis] {
177            set at     [$xmlobj get $path.xaxis.$elem.at]
178            set label  [$xmlobj get $path.xaxis.$elem.label]
179            set styles [$xmlobj get $path.xaxis.$elem.style]
180            lappend _xmarkers [list $at $label $styles]
181        }
182        foreach elem [$xmlobj children -type "marker" $path.yaxis] {
183            set at     [$xmlobj get $path.yaxis.$elem.at]
184            set label  [$xmlobj get $path.yaxis.$elem.label]
185            set styles [$xmlobj get $path.yaxis.$elem.style]
186            lappend _ymarkers [list $at $label $styles]
187        }
188    }
189
190    # ------------------------------------------------------------------
191    # EXPORT: xml
192    # ------------------------------------------------------------------
193    export xml {xmlobj path} {
194        foreach cname [array names _xvecs] {
195            set data ""
196            foreach x [$_xvecs($cname) range 0 end] y [$_yvecs($cname) range 0 end] {
197                append data "$x $y\n"
198            }
199            if {$cname ne ""} {
200                set elem "component($cname)"
201            } else {
202                set elem "component"
203            }
204            $xmlobj put $path.$elem.xy $data
205        }
206    }
207
208    # ------------------------------------------------------------------
209    # COMPARE
210    # ------------------------------------------------------------------
211    compare {
212        if {[array size _xvecs] != [array size _xvecs2]} {
213            return 1  ;# different numbers of components
214        }
215        foreach cname [array names _xvecs] cname2 [array names _xvecs2] {
216            # take a quick look at the x vector
217            set xvlen [$_xvecs($cname) length]
218            if {$xvlen != [$_xvecs2($cname2) length]} {
219                return 1  ;# different lengths of this component
220            }
221
222            # take a quick look at the y vector
223            set yvlen [$_yvecs($cname) length]
224            if {$yvlen != [$_yvecs2($cname2) length]} {
225                return 1  ;# different lengths of this component
226            }
227
228            # scan through all values and see if they match
229            set xv $_xvecs($cname)
230            set xv2 $_xvecs2($cname2)
231            set scale [blt::vector expr {0.5*(abs(max($xv)-min($xv))
232                + abs(max($xv2)-min($xv2)))}]
233            foreach num [$xv range 0 end] num2 [$xv2 range 0 end] {
234                set result [cmpdbl $num $num2 $scale]
235                if {$result != 0} {
236                    return $result
237                }
238            }
239
240            # scan through all values and see if they match
241            set yv $_yvecs($cname)
242            set yv2 $_yvecs2($cname2)
243            set scale [blt::vector expr {0.5*(abs(max($yv)-min($yv))
244                + abs(max($yv2)-min($yv2)))}]
245            foreach num [$yv range 0 end] num2 [$yv2 range 0 end] {
246                set result [cmpdbl $num $num2 $scale]
247                if {$result != 0} {
248                    return $result
249                }
250            }
251        }
252        return 0  ;# same!
253    }
254
255    # ------------------------------------------------------------------
256    # USAGE: components ?<pattern>?
257    #
258    # Returns a list of names for the various components of this
259    # curve.  If the optional glob-style <pattern> is specified, then
260    # it returns only the component names matching the pattern.
261    # ------------------------------------------------------------------
262    method components {{pattern *}} {
263        set rlist ""
264        foreach cname [array names _xvecs] {
265            if {[string match $pattern $cname]} {
266                lappend rlist $cname
267            }
268        }
269        return $rlist
270    }
271
272    # ------------------------------------------------------------------
273    # USAGE: mesh <name>
274    #
275    # Returns the xvec for the specified curve component <name>.
276    # ------------------------------------------------------------------
277    method mesh {cname} {
278        if {[info exists _xvecs($cname)]} {
279            return $_xvecs($cname)  ;# return xv
280        }
281        error "bad option \"$cname\": should be [join [lsort [array names _xvecs]] {, }]"
282    }
283
284    # ------------------------------------------------------------------
285    # USAGE: values <name>
286    #
287    # Returns the yvec for the specified curve component <name>.
288    # ------------------------------------------------------------------
289    method values {cname} {
290        if {[info exists _yvecs($cname)]} {
291            return $_yvecs($cname)  ;# return yv
292        }
293        error "bad option \"$cname\": should be [join [lsort [array names _yvecs]] {, }]"
294    }
295
296    # ------------------------------------------------------------------
297    # USAGE: limits x|xlin|xlog|y|ylin|ylog
298    #
299    # Returns the {min max} limits for the specified axis.
300    # ------------------------------------------------------------------
301    method limits {which} {
302        set min ""
303        set max ""
304        switch -- $which {
305            x - xlin { set pos 0; set log 0; set axis xaxis }
306            xlog { set pos 0; set log 1; set axis xaxis }
307            y - ylin - v - vlin { set pos 1; set log 0; set axis yaxis }
308            ylog - vlog { set pos 1; set log 1; set axis yaxis }
309            default {
310                error "bad option \"$which\": should be x, xlin, xlog, y, ylin, ylog, v, vlin, vlog"
311            }
312        }
313
314        blt::vector create tmp zero
315        foreach comp [array names _xvecs] {
316            set arrvar [lindex {_xvecs _yvecs} $pos]
317            set vname [set ${arrvar}($comp)]
318            $vname variable vec
319
320            if {$log} {
321                # on a log scale, use abs value and ignore 0's
322                $vname dup tmp
323                $vname dup zero
324                zero expr {tmp == 0}            ;# find the 0's
325                tmp expr {abs(tmp)}             ;# get the abs value
326                tmp expr {tmp + zero*max(tmp)}  ;# replace 0's with abs max
327                set vmin [blt::vector expr min(tmp)]
328                set vmax [blt::vector expr max(tmp)]
329            } else {
330                set vmin $vec(min)
331                set vmax $vec(max)
332            }
333
334            if {"" == $min} {
335                set min $vmin
336            } elseif {$vmin < $min} {
337                set min $vmin
338            }
339            if {"" == $max} {
340                set max $vmax
341            } elseif {$vmax > $max} {
342                set max $vmax
343            }
344        }
345        blt::vector destroy tmp zero
346
347        set val [attr get min]
348        if {$val ne "" && $min ne ""} {
349            if {$val > $min} {
350                # tool specified this min -- don't go any lower
351                set min $val
352            }
353        }
354
355        set val [attr get max]
356        if {$val ne "" && $max ne ""} {
357            if {$val < $max} {
358                # tool specified this max -- don't go any higher
359                set max $val
360            }
361        }
362
363        return [list $min $max]
364    }
365
366    # ------------------------------------------------------------------
367    # USAGE: xmarkers
368    #
369    # Returns the list of settings for each marker on the x-axis.
370    # If no markers have been specified the empty string is returned.
371    # ------------------------------------------------------------------
372    method xmarkers {} {
373        return $_xmarkers
374    }
375
376    # ------------------------------------------------------------------
377    # USAGE: ymarkers
378    #
379    # Returns the list of settings for each marker on the y-axis.
380    # If no markers have been specified the empty string is returned.
381    # ------------------------------------------------------------------
382    method ymarkers {} {
383        return $_ymarkers
384    }
385
386    # ------------------------------------------------------------------
387    # USAGE: hints ?keyword?
388    #
389    # This has been replaced by the "attrs" method in the ObjVal
390    # base class, but is provided here for backward-compatibility
391    # with the XyResult viewer.
392    #
393    # With no args, it returns a list of keywords and corresponding
394    # values for all attributes in this object.  If a particular
395    # keyword is specified, then it returns the value for that
396    # attribute.  There are a few more "hints" defined here beyond
397    # the object attributes.
398    # ------------------------------------------------------------------
399    method hints {{keyword ""}} {
400        # first time through, build all of these hint values
401        if {![info exists _hints]} {
402            # start with all of the usual attributes
403            foreach key [attr get] {
404                set _hints($key) [attr get $key]
405            }
406
407            # tweak them a little to produce the values needed for XyResult
408            if {[info exists _hints(xlabel)] && "" != $_hints(xlabel)
409                  && [info exists _hints(xunits)] && "" != $_hints(xunits)} {
410                set _hints(xlabel) "$_hints(xlabel) ($_hints(xunits))"
411            }
412            if {[info exists _hints(ylabel)] && "" != $_hints(ylabel)
413                  && [info exists _hints(yunits)] && "" != $_hints(yunits)} {
414                set _hints(ylabel) "$_hints(ylabel) ($_hints(yunits))"
415            }
416
417            if {[info exists _hints(group)] && [info exists _hints(label)]} {
418                # pop-up help for each curve
419                set _hints(tooltip) $_hints(label)
420            }
421        }
422        if {$keyword != ""} {
423            if {[info exists _hints($keyword)]} {
424                return $_hints($keyword)
425            }
426            return ""
427        }
428        return [array get _hints]
429    }
430}
Note: See TracBrowser for help on using the repository browser.