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 | |
---|
16 | object 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 | } |
---|