source: trunk/gui/scripts/field.tcl @ 3421

Last change on this file since 3421 was 3421, checked in by gah, 12 years ago

push SendCmd? into base class w/ buffering

File size: 44.7 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2
3# ----------------------------------------------------------------------
4#  COMPONENT: field - extracts data from an XML description of a field
5#
6#  This object represents one field in an XML description of a device.
7#  It simplifies the process of extracting data vectors that represent
8#  the field.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16
17# TODO:
18#
19#  o How to describe vector values in a field? 
20#       <components>3</components>
21#       <values></values>
22#
23#    Does anything need to know the limits for each component of the vector?
24#
25
26#
27# Possible field dataset types:
28#
29# 2D Datasets
30#       vtk             (range of z-axis is zero).
31#       unirect2d       (deprecated except where extents > 1)
32#       cloud           (x,y point coordinates) (deprecated)
33#       mesh
34# 3D Datasets
35#       vtk
36#       unirect3d
37#       cloud           (x,y,z coordinates) (deprecated)
38#       mesh
39#       dx              (FIXME: make dx-to-vtk converter work)
40#       ucd avs
41#
42# Viewers:
43#       Format     Dim  Description                     Viewer          Server
44#       vtk         2   vtk file data.                  contour         vtkvis
45#       vtk         3   vtk file data.                  isosurface      vtkvis
46#       mesh        2   points-on-mesh                  heightmap       vtkvis
47#       mesh        3   points-on-mesh                  isosurface      vtkvis
48#       dx          3   DX                              volume          nanovis
49#       unirect2d   2   unirect3d + extents > 1 flow    flow            nanovis
50#       unirect3d   3   unirect2d + extents > 1 flow    flow            nanovis
51#       
52# With <views>, can specify which viewer for a specific datasets.  So it's OK
53# to if the same dataset can be viewed in more than one way.
54#  o Any 2D dataset can be viewed as a contour/heightmap.
55#  o Any 3D dataset can be viewed as a isosurface. 
56#  o Any 2D dataset with vector data can be streamlines. 
57#  o Any 3D uniform rectilinear dataset can be viewed as a volume.
58#  o Any 3D dataset with vector data can be streamlines or flow.
59#
60# Need <views> to properly do things like qdot: volume with a polydata
61# transparent shell.  The view will combine the two objects <field> and
62# <drawing> (??) into a single viewer.
63#
64package require Itcl
65package require BLT
66
67namespace eval Rappture {
68    # forward declaration
69}
70
71itcl::class Rappture::Field {
72    private variable _dim       0;      # Dimension of the mesh
73    private variable _xmlobj ""  ;      # ref to XML obj with field data
74    private variable _limits     ;      # maps box name => {z0 z1} limits
75    private variable _fldObj ""
76    private variable _comp2fields ;     # cname => field names.
77    private variable _field2Components; # field name => number of components
78    private variable _field2Label;      # field name => label
79    private variable _field2Units;      # field name => units
80    private variable _hints
81    private variable _viewer "";        # Hints which viewer to use
82    constructor {xmlobj path} {
83        # defined below
84    }
85    destructor {
86        # defined below
87    }
88    public method components {args}
89    public method mesh {{cname -overall}}
90    public method values {{cname -overall}}
91    public method blob {{cname -overall}}
92    public method limits {axis}
93    public method controls {option args}
94    public method hints {{key ""}}
95    public method style { cname }
96    public method isunirect2d {}
97    public method isunirect3d {}
98    public method extents {{cname -overall}}
99    public method flowhints { cname }
100    public method type {}
101    public method viewer {} {
102        return $_viewer
103    }
104    public method vtkdata {cname}
105    public method fieldnames { cname } {
106        if { ![info exists _comp2fields($cname)] } {
107            return ""
108        }
109        return $_comp2fields($cname)
110    }
111    public method fieldinfo { fname } {
112        lappend out $_field2Label($fname)
113        lappend out $_field2Units($fname)
114        lappend out $_field2Components($fname)
115        return $out
116    }
117    protected method Build {}
118    protected method _getValue {expr}
119
120    private variable _path "";          # Path of this object in the XML
121    private variable _units ""   ;      # system of units for this field
122    private variable _zmax 0     ;# length of the device
123
124    private variable _comp2dims  ;# maps component name => dimensionality
125    private variable _comp2xy    ;# maps component name => x,y vectors
126    private variable _comp2vtk   ;# maps component name => vtk file data
127    private variable _comp2dx    ;# maps component name => OpenDX data
128    private variable _comp2unirect2d ;# maps component name => unirect2d obj
129    private variable _comp2unirect3d ;# maps component name => unirect3d obj
130    private variable _comp2style ;# maps component name => style settings
131    private variable _comp2cntls ;# maps component name => x,y control points
132    private variable _comp2extents
133    private variable _comp2limits;      #  Array of limits per component
134    private variable _type ""
135    private variable _comp2flowhints
136    private variable _comp2mesh
137    private common _counter 0    ;# counter for unique vector names
138
139    private method BuildPointsOnMesh { cname }
140    private method ConvertToVtkData { cname }
141    private method ReadVtkDataSet { cname contents }
142    private method AvsToVtk { cname contents }
143    private variable _values ""
144}
145
146# ----------------------------------------------------------------------
147# CONSTRUCTOR
148# ----------------------------------------------------------------------
149itcl::body Rappture::Field::constructor {xmlobj path} {
150    if {![Rappture::library isvalid $xmlobj]} {
151        error "bad value \"$xmlobj\": should be Rappture::library"
152    }
153    set _xmlobj $xmlobj
154    set _path $path
155    set _fldObj [$xmlobj element -as object $path]
156    set _units [$_fldObj get units]
157
158    set xunits [$xmlobj get units]
159    if {"" == $xunits || "arbitrary" == $xunits} {
160        set xunits "um"
161    }
162
163    # determine the overall size of the device
164    set z0 [set z1 0]
165    foreach elem [$_xmlobj children components] {
166        switch -glob -- $elem {
167            box* {
168                if {![regexp {[0-9]$} $elem]} {
169                    set elem "${elem}0"
170                }
171                set z0 [$_xmlobj get components.$elem.corner0]
172                set z0 [Rappture::Units::convert $z0 \
173                    -context $xunits -to $xunits -units off]
174
175                set z1 [$_xmlobj get components.$elem.corner1]
176                set z1 [Rappture::Units::convert $z1 \
177                    -context $xunits -to $xunits -units off]
178
179                set _limits($elem) [list $z0 $z1]
180            }
181        }
182    }
183    set _zmax $z1
184
185    # build up vectors for various components of the field
186    Build
187}
188
189# ----------------------------------------------------------------------
190# DESTRUCTOR
191# ----------------------------------------------------------------------
192itcl::body Rappture::Field::destructor {} {
193    itcl::delete object $_fldObj
194    # don't destroy the _xmlobj! we don't own it!
195
196    foreach name [array names _comp2xy] {
197        eval blt::vector destroy $_comp2xy($name)
198    }
199    foreach name [array names _comp2unirect2d] {
200        itcl::delete object $_comp2unirect2d($name)
201    }
202    foreach name [array names _comp2unirect3d] {
203        itcl::delete object $_comp2unirect3d($name)
204    }
205    foreach name [array names _comp2flowhints] {
206        itcl::delete object $_comp2flowhints($name)
207    }
208    foreach name [array names _comp2mesh] {
209        # Data is in the form of a mesh and a vector.
210        foreach { mesh vector } $_comp2mesh($name) break
211        # Release the mesh (may be shared)
212        set class [$mesh info class]
213        ${class}::release $mesh
214        # Destroy the vector
215        blt::vector destroy $vector
216    }
217}
218
219# ----------------------------------------------------------------------
220# USAGE: components ?-name|-dimensions|-style? ?<pattern>?
221#
222# Returns a list of names or types for the various components of
223# this field.  If the optional glob-style <pattern> is specified,
224# then it returns only the components with names matching the pattern.
225# ----------------------------------------------------------------------
226itcl::body Rappture::Field::components {args} {
227    Rappture::getopts args params {
228        flag what -name default
229        flag what -dimensions
230        flag what -style
231        flag what -particles
232        flag what -flow
233        flag what -box
234    }
235
236    set pattern *
237    if {[llength $args] > 0} {
238        set pattern [lindex $args 0]
239        set args [lrange $args 1 end]
240    }
241    if {[llength $args] > 0} {
242        error "wrong # args: should be \"components ?switches? ?pattern?\""
243    }
244
245    set rlist ""
246
247    # BE CAREFUL: return component names in proper order
248    foreach cname [$_fldObj children -type component] {
249        if {[info exists _comp2dims($cname)]
250              && [string match $pattern $cname]} {
251
252            switch -- $params(what) {
253                -name { lappend rlist $cname }
254                -dimensions { lappend rlist $_comp2dims($cname) }
255                -style { lappend rlist $_comp2style($cname) }
256            }
257        }
258    }
259    return $rlist
260}
261
262# ----------------------------------------------------------------------
263# USAGE: mesh ?<name>?
264#
265# Returns a list {xvec yvec} for the specified field component <name>.
266# If the name is not specified, then it returns the vectors for the
267# overall field (sum of all components).
268# ----------------------------------------------------------------------
269itcl::body Rappture::Field::mesh {{cname -overall}} {
270    if {$cname == "-overall" || $cname == "component0"} {
271        set cname [lindex [components -name] 0]
272    }
273    if {[info exists _comp2xy($cname)]} {
274        return [lindex $_comp2xy($cname) 0]  ;# return xv
275    }
276    if { [info exists _comp2vtk($cname)] } {
277        # FIXME: extract mesh from VTK file data.
278        error "method \"mesh\" is not implemented for VTK file data"
279    }
280    if {[info exists _comp2dx($cname)]} {
281        return ""  ;# no mesh -- it's embedded in the value data
282    }
283    if {[info exists _comp2mesh($cname)]} {
284        return ""  ;# no mesh -- it's embedded in the value data
285    }
286    if {[info exists _comp2unirect2d($cname)]} {
287        set mobj [lindex $_comp2unirect2d($cname) 0]
288        return [$mobj mesh]
289    }
290    if {[info exists _comp2unirect3d($cname)]} {
291        set mobj [lindex $_comp2unirect3d($cname) 0]
292        return [$mobj mesh]
293    }
294    error "bad option \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
295}
296
297# ----------------------------------------------------------------------
298# USAGE: values ?<name>?
299#
300# Returns a list {xvec yvec} for the specified field component <name>.
301# If the name is not specified, then it returns the vectors for the
302# overall field (sum of all components).
303# ----------------------------------------------------------------------
304itcl::body Rappture::Field::values {{cname -overall}} {
305    if {$cname == "component0"} {
306        set cname "component"
307    }
308    if {[info exists _comp2xy($cname)]} {
309        return [lindex $_comp2xy($cname) 1]  ;# return yv
310    }
311    # VTK file data
312    if { [info exists _comp2vtk($cname)] } {
313        # FIXME: extract the values from the VTK file data
314        error "method \"values\" is not implemented for vtk file data"
315    }
316    # Points-on-mesh
317    if { [info exists _comp2mesh($cname)] } {
318        set vector [lindex $_comp2mesh($cname) 1]
319        return [$vector range 0 end]
320    }
321    if {[info exists _comp2dx($cname)]} {
322        return $_comp2dx($cname)  ;# return gzipped, base64-encoded DX data
323    }
324    if {[info exists _comp2unirect2d($cname)]} {
325        return [$_comp2unirect2d($cname) values]
326    }
327    if {[info exists _comp2unirect3d($cname)]} {
328        return [$_comp2unirect3d($cname) blob]
329    }
330    error "bad option \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
331}
332
333# ----------------------------------------------------------------------
334# USAGE: blob ?<name>?
335#
336# Returns a string representing the blob of data for the mesh and values.
337# ----------------------------------------------------------------------
338itcl::body Rappture::Field::blob {{cname -overall}} {
339    if {$cname == "component0"} {
340        set cname "component"
341    }
342    if {[info exists _comp2xy($cname)]} {
343        return ""
344    }
345    if { [info exists _comp2vtk($cname)] } {
346        error "blob not implemented for VTK file data"
347    }
348    if {[info exists _comp2dx($cname)]} {
349        return $_comp2dx($cname)  ;# return gzipped, base64-encoded DX data
350    }
351    if {[info exists _comp2unirect2d($cname)]} {
352        set blob [$_comp2unirect2d($cname) blob]
353        lappend blob "values" $_values
354        return $blob
355    }
356    if {[info exists _comp2unirect3d($cname)]} {
357        return [$_comp2unirect3d($cname) blob]
358    }
359    error "bad option \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
360}
361
362# ----------------------------------------------------------------------
363# USAGE: limits <axis>
364#
365# Returns a list {min max} representing the limits for the specified
366# axis.
367# ----------------------------------------------------------------------
368itcl::body Rappture::Field::limits {which} {
369    set min ""
370    set max ""
371    blt::vector tmp zero
372
373    foreach cname [array names _comp2dims] {
374        switch -- $_comp2dims($cname) {
375            1D {
376                switch -- $which {
377                    x - xlin {
378                        set pos 0; set log 0; set axis x
379                    }
380                    xlog {
381                        set pos 0; set log 1; set axis x
382                    }
383                    y - ylin - v - vlin {
384                        set pos 1; set log 0; set axis y
385                    }
386                    ylog - vlog {
387                        set pos 1; set log 1; set axis y
388                    }
389                    default {
390                        error "bad option \"$which\": should be x, xlin, xlog, y, ylin, ylog, v, vlin, vlog"
391                    }
392                }
393
394                set vname [lindex $_comp2xy($cname) $pos]
395                $vname variable vec
396
397                if {$log} {
398                    # on a log scale, use abs value and ignore 0's
399                    $vname dup tmp
400                    $vname dup zero
401                    zero expr {tmp == 0}            ;# find the 0's
402                    tmp expr {abs(tmp)}             ;# get the abs value
403                    tmp expr {tmp + zero*max(tmp)}  ;# replace 0's with abs max
404                    set axisMin [blt::vector expr min(tmp)]
405                    set axisMax [blt::vector expr max(tmp)]
406                } else {
407                    set axisMin $vec(min)
408                    set axisMax $vec(max)
409                }
410
411                if {"" == $min} {
412                    set min $axisMin
413                } elseif {$axisMin < $min} {
414                    set min $axisMin
415                }
416                if {"" == $max} {
417                    set max $axisMax
418                } elseif {$axisMax > $max} {
419                    set max $axisMax
420                }
421            }
422            2D - 3D {
423                if {[info exists _comp2unirect3d($cname)]} {
424                    set limits [$_comp2unirect3d($cname) limits $which]
425                    foreach {axisMin axisMax} $limits break
426                    set axis v
427                } elseif {[info exists _comp2limits($cname)]} {
428                    array set limits $_comp2limits($cname)
429                    switch -- $which {
430                        x - xlin - xlog {
431                            set axis x
432                            foreach {axisMin axisMax} $limits(x) break
433                        }
434                        y - ylin - ylog {
435                            set axis y
436                            foreach {axisMin axisMax} $limits(y) break
437                        }
438                        z - zlin - zlog {
439                            set axis y
440                            foreach {axisMin axisMax} $limits(z) break
441                        }
442                        v - vlin - vlog {
443                            set axis v
444                            foreach {axisMin axisMax} $limits(v) break
445                        }
446                        default {
447                            if { ![info exists limits($which)] } {
448                                error "limits: unknown axis \"$which\""
449                            }
450                            set axis v
451                            foreach {axisMin axisMax} $limits($which) break
452                        }
453                    }
454                } else {
455                    set axisMin 0  ;# HACK ALERT! must be OpenDX data
456                    set axisMax 1
457                    set axis v
458                }
459            }
460        }
461        if { "" == $min || $axisMin < $min } {
462            set min $axisMin
463        }
464        if { "" == $max || $axisMax > $max } {
465            set max $axisMax
466        }
467    }
468    blt::vector destroy tmp zero
469    set val [$_fldObj get "${axis}axis.min"]
470    if {"" != $val && "" != $min} {
471        if {$val > $min} {
472            # tool specified this min -- don't go any lower
473            set min $val
474        }
475    }
476    set val [$_fldObj get "${axis}axis.max"]
477    if {"" != $val && "" != $max} {
478        if {$val < $max} {
479            # tool specified this max -- don't go any higher
480            set max $val
481        }
482    }
483    return [list $min $max]
484}
485
486# ----------------------------------------------------------------------
487# USAGE: controls get ?<name>?
488# USAGE: controls validate <path> <value>
489# USAGE: controls put <path> <value>
490#
491# Returns a list {path1 x1 y1 val1  path2 x2 y2 val2 ...} representing
492# control points for the specified field component <name>.
493# ----------------------------------------------------------------------
494itcl::body Rappture::Field::controls {option args} {
495    switch -- $option {
496        get {
497            set cname [lindex $args 0]
498            if {[info exists _comp2cntls($cname)]} {
499                return $_comp2cntls($cname)
500            }
501            return ""
502        }
503        validate {
504            set path [lindex $args 0]
505            set value [lindex $args 1]
506            set units [$_xmlobj get $path.units]
507
508            if {"" != $units} {
509                set nv [Rappture::Units::convert \
510                    $value -context $units -to $units -units off]
511            } else {
512                set nv $value
513            }
514            if {![string is double $nv]
515                  || [regexp -nocase {^(inf|nan)$} $nv]} {
516                error "Value out of range"
517            }
518
519            set rawmin [$_xmlobj get $path.min]
520            if {"" != $rawmin} {
521                set minv $rawmin
522                if {"" != $units} {
523                    set minv [Rappture::Units::convert \
524                        $minv -context $units -to $units -units off]
525                    set nv [Rappture::Units::convert \
526                        $value -context $units -to $units -units off]
527                }
528                # fix for the case when the user tries to
529                # compare values like minv=-500 nv=-0600
530                set nv [format "%g" $nv]
531                set minv [format "%g" $minv]
532
533                if {$nv < $minv} {
534                    error "Minimum value allowed here is $rawmin"
535                }
536            }
537
538            set rawmax [$_xmlobj get $path.max]
539            if {"" != $rawmax} {
540                set maxv $rawmax
541                if {"" != $units} {
542                    set maxv [Rappture::Units::convert \
543                        $maxv -context $units -to $units -units off]
544                    set nv [Rappture::Units::convert \
545                        $value -context $units -to $units -units off]
546                }
547                # fix for the case when the user tries to
548                # compare values like maxv=-500 nv=-0600
549                set nv [format "%g" $nv]
550                set maxv [format "%g" $maxv]
551
552                if {$nv > $maxv} {
553                    error "Maximum value allowed here is $rawmax"
554                }
555            }
556
557            return "ok"
558        }
559        put {
560            set path [lindex $args 0]
561            set value [lindex $args 1]
562            $_xmlobj put $path.current $value
563            Build
564        }
565        default {
566            error "bad option \"$option\": should be get or put"
567        }
568    }
569}
570
571# ----------------------------------------------------------------------
572# USAGE: hints ?<keyword>?
573#
574# Returns a list of key/value pairs for various hints about plotting
575# this field.  If a particular <keyword> is specified, then it returns
576# the hint for that <keyword>, if it exists.
577# ----------------------------------------------------------------------
578itcl::body Rappture::Field::hints {{keyword ""}} {
579    if { ![info exists _hints] } {
580        foreach {key path} {
581            camera          camera.position
582            color           about.color
583            default         about.default
584            group           about.group
585            label           about.label
586            scale           about.scale
587            seeds           about.seeds
588            style           about.style
589            type            about.type
590            xlabel          about.xaxis.label
591            ylabel          about.yaxis.label
592            zlabel          about.zaxis.label
593            xunits          about.xaxis.units
594            yunits          about.yaxis.units
595            zunits          about.zaxis.units
596            units           units
597            updir           updir
598            vectors         about.vectors
599        } {
600            set str [$_fldObj get $path]
601            if { "" != $str } {
602                set _hints($key) $str
603            }
604        }
605        foreach {key path} {
606            toolid          tool.id
607            toolname        tool.name
608            toolcommand     tool.execute
609            tooltitle       tool.title
610            toolrevision    tool.version.application.revision
611        } {
612            set str [$_xmlobj get $path]
613            if { "" != $str } {
614                set _hints($key) $str
615            }
616        }
617        # Set toolip and path hints
618        set _hints(path) $_path
619        if { [info exists _hints(group)] && [info exists _hints(label)] } {
620            # pop-up help for each curve
621            set _hints(tooltip) $_hints(label)
622        }
623    }
624    if { $keyword != "" } {
625        if {[info exists _hints($keyword)]} {
626            return $_hints($keyword)
627        }
628        return ""
629    }
630    return [array get _hints]
631}
632
633# ----------------------------------------------------------------------
634# USAGE: Build
635#
636# Used internally to build up the vector representation for the
637# field when the object is first constructed, or whenever the field
638# data changes.  Discards any existing vectors and builds everything
639# from scratch.
640# ----------------------------------------------------------------------
641itcl::body Rappture::Field::Build {} {
642
643    # Discard any existing data
644    foreach name [array names _comp2xy] {
645        eval blt::vector destroy $_comp2xy($name)
646    }
647    array unset _comp2vtk
648    foreach name [array names _comp2unirect2d] {
649        eval itcl::delete object $_comp2unirect2d($name)
650    }
651    foreach name [array names _comp2unirect3d] {
652        eval itcl::delete object $_comp2unirect3d($name)
653    }
654    catch {unset _comp2xy}
655    catch {unset _comp2dx}
656    catch {unset _comp2dims}
657    catch {unset _comp2style}
658    array unset _comp2unirect2d
659    array unset _comp2unirect3d
660    array unset _comp2extents
661    array unset _dataobj2type
662    #
663    # Scan through the components of the field and create
664    # vectors for each part.
665    #
666    foreach cname [$_fldObj children -type component] {
667        set type ""
668        if { ([$_fldObj element $cname.constant] != "" &&
669              [$_fldObj element $cname.domain] != "") ||
670              [$_fldObj element $cname.xy] != "" } {
671            set type "1D"
672        } elseif { [$_fldObj element $cname.mesh] != "" &&
673                   [$_fldObj element $cname.values] != ""} {
674            set type "points-on-mesh"
675        } elseif { [$_fldObj element $cname.vtk] != ""} {
676            set viewer [$_fldObj get "about.view"]
677            set type "vtk"
678            if { $viewer != "" } {
679                set _viewer $viewer
680            }
681        } elseif {[$_fldObj element $cname.opendx] != ""} {
682            global env
683            if { [info exists env(VTKVOLUME)] } {
684                set type "vtkvolume"
685            } else {
686                set type "dx"
687            }
688        } elseif {[$_fldObj element $cname.dx] != ""} {
689            global env
690            if { [info exists env(VTKVOLUME)] } {
691                set type "vtkvolume"
692            } else {
693                set type "dx"
694            }
695        }
696        set _comp2style($cname) ""
697       
698        # Save the extents of the component
699        if { [$_fldObj element $cname.extents] != "" } {
700            set extents [$_fldObj get $cname.extents]
701        } else {
702            set extents 1
703        }
704        set _comp2extents($cname) $extents
705        set _type $type
706        if {$type == "1D"} {
707            #
708            # 1D data can be represented as 2 BLT vectors,
709            # one for x and the other for y.
710            #
711            set xv ""
712            set yv ""
713
714            set val [$_fldObj get $cname.constant]
715            if {$val != ""} {
716                set domain [$_fldObj get $cname.domain]
717                if {$domain == "" || ![info exists _limits($domain)]} {
718                    set z0 0
719                    set z1 $_zmax
720                } else {
721                    foreach {z0 z1} $_limits($domain) { break }
722                }
723                set xv [blt::vector create x$_counter]
724                $xv append $z0 $z1
725
726                foreach {val pcomp} [_getValue $val] break
727                set yv [blt::vector create y$_counter]
728                $yv append $val $val
729
730                if {$pcomp != ""} {
731                    set zm [expr {0.5*($z0+$z1)}]
732                    set _comp2cntls($cname) \
733                        [list $pcomp $zm $val "$val$_units"]
734                }
735            } else {
736                set xydata [$_fldObj get $cname.xy]
737                if {"" != $xydata} {
738                    set xv [blt::vector create x$_counter]
739                    set yv [blt::vector create y$_counter]
740                    set tmp [blt::vector create \#auto]
741                    $tmp set $xydata
742                    $tmp split $xv $yv
743                    blt::vector destroy $tmp
744                }
745            }
746
747            if {$xv != "" && $yv != ""} {
748                # sort x-coords in increasing order
749                $xv sort $yv
750                set _comp2dims($cname) "1D"
751                set _comp2xy($cname) [list $xv $yv]
752                incr _counter
753            }
754        } elseif {$type == "points-on-mesh"} {
755            BuildPointsOnMesh $cname
756        } elseif {$type == "vtk"} {
757            set vtkdata [$_fldObj get $cname.vtk]
758            ReadVtkDataSet $cname $vtkdata
759            set _comp2vtk($cname) $vtkdata
760            set _comp2style($cname) [$_fldObj get $cname.style]
761            incr _counter
762        } elseif {$type == "dx" } {
763            #
764            # HACK ALERT!  Extract gzipped, base64-encoded OpenDX
765            # data.  Assume that it's 3D.  Pass it straight
766            # off to the NanoVis visualizer.
767            #
768            set _viewer "nanovis"
769            set _comp2dims($cname) "3D"
770            set _comp2dx($cname)  [$_fldObj get -decode no $cname.dx]
771            if 0 {
772                set data  [$_fldObj get -decode yes $cname.dx]
773                set file "/tmp/junk.dx"
774                set f [open $file "w"]
775                puts $f $data
776                close $f
777                if { [string match "<ODX>*" $data] } {
778                    set data [string range $data 5 end]
779                    set _comp2dx($cname) \
780                        [Rappture::encoding::encode -as zb64 $data]
781                }
782            }
783            set _comp2style($cname) [$_fldObj get $cname.style]
784            if {[$_fldObj element $cname.flow] != ""} {
785                set _comp2flowhints($cname) \
786                    [Rappture::FlowHints ::\#auto $_fldObj $cname $_units]
787            }
788            incr _counter
789        } elseif {$type == "opendx"} {
790            #
791            # HACK ALERT!  Extract gzipped, base64-encoded OpenDX
792            # data.  Assume that it's 3D.  Pass it straight
793            # off to the NanoVis visualizer.
794            #
795            set _viewer "nanovis"
796            set _comp2dims($cname) "3D"
797            set data [$_fldObj get -decode yes $cname.opendx]
798            set data "<ODX>$data"
799            set data [Rappture::encoding::encode -as zb64 $data]
800            set _comp2dx($cname) $data
801            set _comp2style($cname) [$_fldObj get $cname.style]
802            if {[$_fldObj element $cname.flow] != ""} {
803                set _comp2flowhints($cname) \
804                    [Rapture::FlowHints ::\#auto $_fldObj $cname $_units]
805            }
806            incr _counter
807        } elseif {[$_fldObj element $cname.ucd] != ""} {
808            set _viewer "isosurface"
809            set _comp2dims($cname) "3D"
810            set contents [$_fldObj get $cname.ucd]
811            set vtkdata [AvsToVtk $cname $contents]
812            ReadVtkDataSet $cname $vtkdata
813            set _comp2vtk($cname) $vtkdata
814            set _comp2style($cname) [$_fldObj get $cname.style]
815            incr _counter
816        }
817    }
818    # Sanity check.  Verify that all components of the field have the same
819    # dimension.
820    set dim ""
821    foreach cname [array names _comp2dims] {
822        if { $dim == "" } {
823            set dim $_comp2dims($cname)
824            continue
825        }
826        if { $dim != $_comp2dims($cname) } {
827            error "field can't have components of different dimensions: [join [array get _comp2dims] ,]"
828        }
829    }
830    # FIXME: about.scalars and about.vectors are temporary.  With views
831    #        the label and units for each field will be specified there.
832    #
833    # FIXME: Test that every <field><component> has the same field names,
834    #        units, components.
835    #
836    # Override what we found in the VTK file with names that the user
837    # selected.  We override the field label and units.
838    foreach { fname label units } [$_fldObj get about.scalars] {
839        if { ![info exists _field2Name($fname)] } {
840            set _field2Name($fname) $fname
841            set _field2Components($fname) 1
842        }
843        set _field2Label($fname) $label
844        set _field2Units($fname) $units
845    }
846    foreach { fname label units } [$_fldObj get about.vectors] {
847        if { ![info exists _field2Name($fname)] } {
848            set _field2Name($fname) $fname
849            # We're just marking the field as vector (> 1) for now.
850            set _field2Components($fname) 3
851        }
852        set _field2Label($fname) $label
853        set _field2Units($fname) $units
854    }
855}
856
857# ----------------------------------------------------------------------
858# USAGE: _getValue <expr>
859#
860# Used internally to get the value for an expression <expr>.  Returns
861# a list of the form {val parameterPath}, where val is the numeric
862# value of the expression, and parameterPath is the XML path to the
863# parameter representing the value, or "" if the <expr> does not
864# depend on any parameters.
865# ----------------------------------------------------------------------
866itcl::body Rappture::Field::_getValue {expr} {
867    #
868    # First, look for the expression among the <parameter>'s
869    # associated with the device.
870    #
871    set found 0
872    foreach pcomp [$_xmlobj children parameters] {
873        set id [$_xmlobj element -as id parameters.$pcomp]
874        if {[string equal $id $expr]} {
875            set val [$_xmlobj get parameters.$pcomp.current]
876            if {"" == $val} {
877                set val [$_xmlobj get parameters.$pcomp.default]
878            }
879            if {"" != $val} {
880                set expr $val
881                set found 1
882                break
883            }
884        }
885    }
886    if {$found} {
887        set pcomp "parameters.$pcomp"
888    } else {
889        set pcomp ""
890    }
891
892    if {$_units != ""} {
893        set expr [Rappture::Units::convert $expr \
894            -context $_units -to $_units -units off]
895    }
896
897    return [list $expr $pcomp]
898}
899
900#
901# isunirect2d  --
902#
903# Returns if the field is a unirect2d object. 
904#
905itcl::body Rappture::Field::isunirect2d { } {
906    return [expr [array size _comp2unirect2d] > 0]
907}
908
909#
910# isunirect3d  --
911#
912# Returns if the field is a unirect3d object. 
913#
914itcl::body Rappture::Field::isunirect3d { } {
915    return [expr [array size _comp2unirect3d] > 0]
916}
917
918#
919# flowhints  --
920#
921# Returns the hints associated with a flow vector field. 
922#
923itcl::body Rappture::Field::flowhints { cname } {
924    if { [info exists _comp2flowhints($cname)] } {
925        return $_comp2flowhints($cname)
926    }
927    return ""
928}
929
930#
931# style  --
932#
933# Returns the style associated with a component of the field. 
934#
935itcl::body Rappture::Field::style { cname } {
936    if { [info exists _comp2style($cname)] } {
937        return $_comp2style($cname)
938    }
939    return ""
940}
941
942#
943# type  --
944#
945# Returns the style associated with a component of the field. 
946#
947itcl::body Rappture::Field::type {} {
948    return $_type
949}
950
951#
952# extents --
953#
954# Returns if the field is a unirect2d object. 
955#
956itcl::body Rappture::Field::extents {{cname -overall}} {
957    if {$cname == "-overall" } {
958        set max 0
959        foreach cname [$_fldObj children -type component] {
960            if { ![info exists _comp2unirect3d($cname)] &&
961                 ![info exists _comp2extents($cname)] } {
962                continue
963            }
964            set value $_comp2extents($cname)
965            if { $max < $value } {
966                set max $value
967            }
968        }
969        return $max
970    }
971    if { $cname == "component0"} {
972        set cname [lindex [components -name] 0]
973    }
974    return $_comp2extents($cname)
975}
976
977itcl::body Rappture::Field::ConvertToVtkData { cname } {
978    set ds ""
979    switch -- [typeof $cname] {
980        "unirect2d" {
981            foreach { x1 x2 xN y1 y2 yN } [$dataobj mesh $cname] break
982            set spacingX [expr {double($x2 - $x1)/double($xN - 1)}]
983            set spacingY [expr {double($y2 - $y1)/double($yN - 1)}]
984           
985            set ds [vtkImageData $this-grdataTemp]
986            $ds SetDimensions $xN $yN 1
987            $ds SetOrigin $x1 $y1 0
988            $ds SetSpacing $spacingX $spacingY 0
989            set arr [vtkDoubleArray $this-arrTemp]
990            foreach {val} [$dataobj values $cname] {
991                $arr InsertNextValue $val
992            }
993            [$ds GetPointData] SetScalars $arr
994        }
995        "unirect3d" {
996            foreach { x1 x2 xN y1 y2 yN z1 z2 zN } [$dataobj mesh $cname] break
997            set spacingX [expr {double($x2 - $x1)/double($xN - 1)}]
998            set spacingY [expr {double($y2 - $y1)/double($yN - 1)}]
999            set spacingZ [expr {double($z2 - $z1)/double($zN - 1)}]
1000           
1001            set ds [vtkImageData $this-grdataTemp]
1002            $ds SetDimensions $xN $yN $zN
1003            $ds SetOrigin $x1 $y1 $z1
1004            $ds SetSpacing $spacingX $spacingY $spacingZ
1005            set arr [vtkDoubleArray $this-arrTemp]
1006            foreach {val} [$dataobj values $cname] {
1007                $arr InsertNextValue $val
1008            }
1009            [$ds GetPointData] SetScalars $val
1010        }
1011        "contour" {
1012            return [$dataobj blob $cname]
1013        }
1014        "dx" {
1015            return [Rappture::ConvertDxToVtk $_comp2dx($cname)]
1016        }
1017        default {
1018            set mesh [$dataobj mesh $cname]
1019            switch -- [$mesh GetClassName] {
1020                vtkPoints {
1021                    # handle cloud of points
1022                    set ds [vtkPolyData $this-polydataTemp]
1023                    $ds SetPoints $mesh
1024                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1025                }
1026                vtkPolyData {
1027                    set ds [vtkPolyData $this-polydataTemp]
1028                    $ds ShallowCopy $mesh
1029                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1030                }
1031                vtkUnstructuredGrid {
1032                    # handle 3D grid with connectivity
1033                    set ds [vtkUnstructuredGrid $this-grdataTemp]
1034                    $ds ShallowCopy $mesh
1035                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1036                }
1037                vtkRectilinearGrid {
1038                    # handle 3D grid with connectivity
1039                    set ds [vtkRectilinearGrid $this-grdataTemp]
1040                    $ds ShallowCopy $mesh
1041                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1042                }
1043                default {
1044                    error "don't know how to handle [$mesh GetClassName] data"
1045                }
1046            }
1047        }
1048    }
1049
1050    if {"" != $ds} {
1051        set writer [vtkDataSetWriter $this-dsWriterTmp]
1052        $writer SetInput $ds
1053        $writer SetFileTypeToASCII
1054        $writer WriteToOutputStringOn
1055        $writer Write
1056        set out [$writer GetOutputString]
1057        $ds Delete
1058        $writer Delete
1059    } else {
1060        set out ""
1061        error "No DataSet to write"
1062    }
1063
1064    append out "\n"
1065    return $out
1066}
1067
1068itcl::body Rappture::Field::ReadVtkDataSet { cname contents } {
1069    package require vtk
1070
1071    set reader $this-datasetreader
1072    vtkDataSetReader $reader
1073
1074    # Write the contents to a file just in case it's binary.
1075    set tmpfile file[pid].vtk
1076    set f [open "$tmpfile" "w"]
1077    fconfigure $f -translation binary -encoding binary
1078    puts $f $contents
1079    close $f
1080
1081    $reader SetFileName $tmpfile
1082    $reader ReadAllScalarsOn
1083    $reader ReadAllVectorsOn
1084    $reader ReadAllFieldsOn
1085    $reader Update
1086    set dataset [$reader GetOutput]
1087    set limits {}
1088    foreach {xmin xmax ymin ymax zmin zmax} [$dataset GetBounds] break
1089    # Figure out the dimension of the mesh from the bounds.
1090    set _dim 0
1091    if { $xmax > $xmin } {
1092        incr _dim
1093    }
1094    if { $ymax > $ymin } {
1095        incr _dim
1096    }
1097    if { $zmax > $zmin } {
1098        incr _dim
1099    }
1100    if { $_viewer == "" } {
1101        if { $_dim == 2 } {
1102            set _viewer contour
1103        } else {
1104            set _viewer isosurface
1105        }
1106    }
1107    set _comp2dims($cname) ${_dim}D
1108    lappend limits x [list $xmin $xmax]
1109    lappend limits y [list $ymin $ymax]
1110    lappend limits z [list $zmin $zmax]
1111    set dataAttrs [$dataset GetPointData]
1112    if { $dataAttrs == ""} {
1113        puts stderr "No point data"
1114    }
1115    set vmin 0
1116    set vmax 1
1117    set numArrays [$dataAttrs GetNumberOfArrays]
1118    if { $numArrays > 0 } {
1119        set array [$dataAttrs GetArray 0]
1120        foreach {vmin vmax} [$array GetRange] break
1121
1122        for {set i 0} {$i < [$dataAttrs GetNumberOfArrays] } {incr i} {
1123            set array [$dataAttrs GetArray $i]
1124            set fname  [$dataAttrs GetArrayName $i]
1125            foreach {min max} [$array GetRange] break
1126            lappend limits $fname [list $min $max]
1127            set _field2Units($fname) ""
1128            set _field2Label($fname) $fname
1129            set _field2Components($fname) [$array GetNumberOfComponents]
1130            lappend _comp2fields($cname) $fname
1131        }
1132    }
1133    lappend limits v [list $vmin $vmax]
1134    set _comp2limits($cname) $limits
1135    file delete $tmpfile
1136    rename $reader ""
1137}
1138
1139#
1140# vtkdata --
1141#
1142#       Returns a string representing the mesh and field data for a specific
1143#       component in the legacy VTK file format.
1144#
1145itcl::body Rappture::Field::vtkdata {cname} {
1146    if {$cname == "component0"} {
1147        set cname "component"
1148    }
1149    # DX: Convert DX to VTK
1150    if {[info exists _comp2dx($cname)]} {
1151        return [Rappture::ConvertDxToVtk $_comp2dx($cname)]
1152    }
1153    # Unirect3d: isosurface
1154    if {[info exists _comp2unirect3d($cname)]} {
1155        return [$_comp2unirect3d($cname) vtkdata]
1156    }
1157    # VTK file data:
1158    if { [info exists _comp2vtk($cname)] } {
1159        return $_comp2vtk($cname)
1160    }
1161    # Points on mesh:  Construct VTK file output.
1162    if { [info exists _comp2mesh($cname)] } {
1163        # Data is in the form mesh and vector
1164        foreach {mesh vector} $_comp2mesh($cname) break
1165        set label [hints zlabel]
1166        if { $label == "" } {
1167            set label $cname
1168            if { [regexp {component\((.*)\)} $label match subname] } {
1169                set label $subname
1170            }
1171        }
1172        regsub -all { } $label {_} label
1173        append out "# vtk DataFile Version 3.0\n"
1174        append out "[hints label]\n"
1175        append out "ASCII\n"
1176        append out [$mesh vtkdata]
1177        append out "POINT_DATA [$vector length]\n"
1178        append out "SCALARS $label double\n"
1179        append out "LOOKUP_TABLE default\n"
1180        append out "[$vector range 0 end]\n"
1181        return $out
1182    }
1183    error "can't find vtkdata for $cname. This method should only be called by the vtkheightmap widget"
1184}
1185
1186#
1187# BuildPointsOnMesh --
1188#
1189#       Parses the field XML description to build a mesh and values vector
1190#       representing the field.  Right now we handle the deprecated types
1191#       of "cloud", "unirect2d", and "unirect3d" (mostly for flows).
1192#
1193itcl::body Rappture::Field::BuildPointsOnMesh {cname} {
1194    #
1195    # More complex 2D/3D data is represented by a mesh
1196    # object and an associated vector for field values.
1197    #
1198    set path [$_fldObj get $cname.mesh]
1199    if {[$_xmlobj element $path] == ""} {
1200        # Unknown mesh designated.
1201        return
1202    }
1203    set element [$_xmlobj element -as type $path]
1204    set name $cname
1205    if { [regexp {component\((.*)\)} $name match subname] } {
1206        set name $subname
1207    }
1208    regsub -all { } $name {_} name
1209    set _field2Label($name) $name
1210    set label [hints zlabel]
1211    if { $label != "" } {
1212        set _field2Label($name) $label
1213    }
1214    set _field2Units($name) [hints zlabel]
1215    set _field2Components($name) 1
1216    lappend _comp2fields($cname) $name
1217
1218    # Handle bizarre cases that hopefully will be deprecated.
1219    if { $element == "unirect3d" } {
1220        # Special case: unirect3d (should be deprecated) + flow.
1221        if { [$_fldObj element $cname.extents] != "" } {
1222            set extents [$_fldObj get $cname.extents]
1223        } else {
1224            set extents 1
1225        }
1226        set _dim 3
1227        set _viewer flowvis
1228        set _comp2dims($cname) "3D"
1229        set _comp2unirect3d($cname) \
1230            [Rappture::Unirect3d \#auto $_xmlobj $_fldObj $cname $extents]
1231        set _comp2style($cname) [$_fldObj get $cname.style]
1232        if {[$_fldObj element $cname.flow] != ""} {
1233            set _comp2flowhints($cname) \
1234                [Rappture::FlowHints ::\#auto $_fldObj $cname $_units]
1235        }
1236        incr _counter
1237        return
1238    }
1239    if { $element == "unirect2d" && [$_fldObj element $cname.flow] != "" } {
1240        # Special case: unirect2d (normally deprecated) + flow.
1241        if { [$_fldObj element $cname.extents] != "" } {
1242            set extents [$_fldObj get $cname.extents]
1243        } else {
1244            set extents 1
1245        }
1246        set _dim 2
1247        set _viewer "flowvis"
1248        set _comp2dims($cname) "2D"
1249        set _comp2unirect2d($cname) \
1250            [Rappture::Unirect2d \#auto $_xmlobj $path]
1251        set _comp2style($cname) [$_fldObj get $cname.style]
1252        set _comp2flowhints($cname) \
1253            [Rappture::FlowHints ::\#auto $_fldObj $cname $_units]
1254        set _values [$_fldObj get $cname.values]
1255        set limits {}
1256        foreach axis { x y } {
1257            lappend limits $axis [$_comp2unirect2d($cname) limits $axis]
1258        }
1259        set xv [blt::vector create \#auto]
1260        $xv set $_values
1261        lappend limits "v" [$xv limits]
1262        blt::vector destroy $xv
1263        set _comp2limits($cname) $limits
1264        incr _counter
1265        return
1266    }
1267    set _viewer "contour"
1268    switch -- $element {
1269        "cloud" {
1270            set mesh [Rappture::Cloud::fetch $_xmlobj $path]
1271        }
1272        "mesh" {
1273            set mesh [Rappture::Mesh::fetch $_xmlobj $path]
1274        }           
1275        "unirect2d" {
1276            set mesh [Rappture::Unirect2d::fetch $_xmlobj $path]
1277            set _viewer "heightmap"
1278        }
1279    }
1280    set _dim [$mesh dimensions]
1281    if {$_dim == 1} {
1282        # Is this used anywhere?
1283        #
1284        # OOPS!  This is 1D data
1285        # Forget the cloud/field -- store BLT vectors
1286        #
1287        # Is there a natural growth path in generating output from 1D to
1288        # higher dimensions?  If there isn't, let's kill this in favor
1289        # or explicitly using a <curve> instead.  Otherwise, the features
1290        # (methods such as xmarkers) or the <curve> need to be added
1291        # to the <field>.
1292        #
1293        set xv [blt::vector create x$_counter]
1294        set yv [blt::vector create y$_counter]
1295       
1296        $yv set [$mesh points]
1297        $xv seq 0 1 [$yv length]
1298        # sort x-coords in increasing order
1299        $xv sort $yv
1300       
1301        set _comp2dims($cname) "1D"
1302        set _comp2xy($cname) [list $xv $yv]
1303        incr _counter
1304        return
1305    } elseif {$_dim == 2} {
1306        set _type "heightmap"
1307        set vector [blt::vector create \#auto]
1308        $vector set [$_fldObj get $cname.values]
1309        set _comp2dims($cname) "[$mesh dimensions]D"
1310        set _comp2mesh($cname) [list $mesh $vector]
1311        set _comp2style($cname) [$_fldObj get $cname.style]
1312        incr _counter
1313        array unset _comp2limits $cname
1314        lappend _comp2limits($cname) x [$mesh limits x]
1315        lappend _comp2limits($cname) y [$mesh limits y]
1316        lappend _comp2limits($cname) $label [$vector limits]
1317        lappend _comp2limits($cname) v [$vector limits]
1318        return
1319    } elseif {$_dim == 3} {
1320        #
1321        # 3D data: Store cloud/field as components
1322        #
1323        set values [$_fldObj get $cname.values]
1324        set farray [vtkFloatArray ::vals$_counter]
1325        foreach v $values {
1326            if {"" != $_units} {
1327                set v [Rappture::Units::convert $v \
1328                           -context $_units -to $_units -units off]
1329            }
1330            $farray InsertNextValue $v
1331        }
1332        set _viewer "isosurface"
1333        set _type "isosurface"
1334        set vector [blt::vector create \#auto]
1335        $vector set [$_fldObj get $cname.values]
1336        set _comp2dims($cname) "[$mesh dimensions]D"
1337        set _comp2mesh($cname) [list $mesh $vector]
1338        set _comp2style($cname) [$_fldObj get $cname.style]
1339        incr _counter
1340        lappend _comp2limits($cname) x [$mesh limits x]
1341        lappend _comp2limits($cname) y [$mesh limits y]
1342        lappend _comp2limits($cname) z [$mesh limits z]
1343        lappend _comp2limits($cname) $label [$vector limits]
1344        lappend _comp2limits($cname) v [$vector limits]
1345        return
1346    }
1347    error "unhandled case in field dim=$_dim element=$element"
1348}
1349
1350itcl::body Rappture::Field::AvsToVtk { comp contents } {
1351    package require vtk
1352
1353    set reader $this-datasetreader
1354    vtkAVSucdReader $reader
1355
1356    # Write the contents to a file just in case it's binary.
1357    set tmpfile file[pid].vtk
1358    set f [open "$tmpfile" "w"]
1359    fconfigure $f -translation binary -encoding binary
1360    puts $f $contents
1361    close $f
1362    $reader SetFileName $tmpfile
1363    $reader Update
1364    file delete $tmpfile
1365
1366    set output [$reader GetOutput]
1367    set pointData [$output GetPointData]
1368    set _scalars {}
1369    for { set i 0 } { $i < [$pointData GetNumberOfArrays] } { incr i } {
1370        set name [$pointData GetArrayName $i]
1371        lappend _scalars $name $name "???"
1372    }
1373    set writer $this-datasetwriter
1374    vtkDataSetWriter $writer
1375    $writer SetInputConnection [$reader GetOutputPort]
1376    $writer SetFileName $tmpfile
1377    $writer Write
1378    rename $reader ""
1379    rename $writer ""
1380    set f [open "$tmpfile" "r"]
1381    fconfigure $f -translation binary -encoding binary
1382    set vtkdata [read $f]
1383    close $f
1384    file delete $tmpfile
1385    return $vtkdata
1386}
Note: See TracBrowser for help on using the repository browser.