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

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

add development notes to field.

File size: 44.4 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 1 {
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    $reader SetFileName $tmpfile
1081    $reader ReadAllScalarsOn
1082    $reader ReadAllVectorsOn
1083    $reader ReadAllFieldsOn
1084    $reader Update
1085    set dataset [$reader GetOutput]
1086    set limits {}
1087    foreach {xmin xmax ymin ymax zmin zmax} [$dataset GetBounds] break
1088    # Figure out the dimension of the mesh from the bounds.
1089    set _dim 0
1090    if { $xmax > $xmin } {
1091        incr _dim
1092    }
1093    if { $ymax > $ymin } {
1094        incr _dim
1095    }
1096    if { $zmax > $zmin } {
1097        incr _dim
1098    }
1099    if { $_viewer == "" } {
1100        if { $_dim == 2 } {
1101            set _viewer contour
1102        } else {
1103            set _viewer isosurface
1104        }
1105    }
1106    set _comp2dims($cname) ${_dim}D
1107    lappend limits x [list $xmin $xmax]
1108    lappend limits y [list $ymin $ymax]
1109    lappend limits z [list $zmin $zmax]
1110    set dataAttrs [$dataset GetPointData]
1111    if { $dataAttrs == ""} {
1112        puts stderr "No point data"
1113    }
1114    set vmin 0
1115    set vmax 1
1116    set numArrays [$dataAttrs GetNumberOfArrays]
1117    if { $numArrays > 0 } {
1118        set array [$dataAttrs GetArray 0]
1119        foreach {vmin vmax} [$array GetRange] break
1120
1121        for {set i 0} {$i < [$dataAttrs GetNumberOfArrays] } {incr i} {
1122            set array [$dataAttrs GetArray $i]
1123            set fname  [$dataAttrs GetArrayName $i]
1124            foreach {min max} [$array GetRange] break
1125            lappend limits $fname [list $min $max]
1126            set _field2Units($fname) ""
1127            set _field2Label($fname) $fname
1128            set _field2Components($fname) [$array GetNumberOfComponents]
1129            lappend _comp2fields($cname) $fname
1130        }
1131    }
1132    lappend limits v [list $vmin $vmax]
1133    set _comp2limits($cname) $limits
1134    $reader Delete
1135    file delete $tmpfile
1136}
1137
1138#
1139# vtkdata --
1140#
1141#       Returns a string representing the mesh and field data for a specific
1142#       component in the legacy VTK file format.
1143#
1144itcl::body Rappture::Field::vtkdata {cname} {
1145    if {$cname == "component0"} {
1146        set cname "component"
1147    }
1148    # DX: Convert DX to VTK
1149    if {[info exists _comp2dx($cname)]} {
1150        return [Rappture::ConvertDxToVtk $_comp2dx($cname)]
1151    }
1152    # Unirect3d: isosurface
1153    if {[info exists _comp2unirect3d($cname)]} {
1154        return [$_comp2unirect3d($cname) vtkdata]
1155    }
1156    # VTK file data:
1157    if { [info exists _comp2vtk($cname)] } {
1158        return $_comp2vtk($cname)
1159    }
1160    # Points on mesh:  Construct VTK file output.
1161    if { [info exists _comp2mesh($cname)] } {
1162        # Data is in the form mesh and vector
1163        foreach {mesh vector} $_comp2mesh($cname) break
1164        set label [hints zlabel]
1165        if { $label == "" } {
1166            set label $cname
1167        } else {
1168            regsub -all { } $label {_} label
1169        }
1170        append out "# vtk DataFile Version 3.0\n"
1171        append out "[hints label]\n"
1172        append out "ASCII\n"
1173        append out [$mesh vtkdata]
1174        append out "POINT_DATA [$vector length]\n"
1175        append out "SCALARS $label float\n"
1176        append out "LOOKUP_TABLE default\n"
1177        append out "[$vector range 0 end]\n"
1178        return $out
1179    }
1180    error "can't find vtkdata for $cname. This method should only be called by the vtkheightmap widget"
1181}
1182
1183#
1184# BuildPointsOnMesh --
1185#
1186#       Parses the field XML description to build a mesh and values vector
1187#       representing the field.  Right now we handle the deprecated types
1188#       of "cloud", "unirect2d", and "unirect3d" (mostly for flows).
1189#
1190itcl::body Rappture::Field::BuildPointsOnMesh {cname} {
1191    #
1192    # More complex 2D/3D data is represented by a mesh
1193    # object and an associated vector for field values.
1194    #
1195    set path [$_fldObj get $cname.mesh]
1196    if {[$_xmlobj element $path] == ""} {
1197        # Unknown mesh designated.
1198        return
1199    }
1200    set element [$_xmlobj element -as type $path]
1201    set name $cname
1202    regsub -all { } $name {_} name
1203    set _field2Label($name) $cname
1204    set label [hints zlabel]
1205    if { $label != "" } {
1206        set _field2Label($name) $label
1207    }
1208    set _field2Units($name) [hints zlabel]
1209    set _field2Components($name) 1
1210    lappend _comp2fields($cname) $name
1211
1212    # Handle bizarre cases that hopefully will be deprecated.
1213    if { $element == "unirect3d" } {
1214        # Special case: unirect3d (should be deprecated) + flow.
1215        if { [$_fldObj element $cname.extents] != "" } {
1216            set extents [$_fldObj get $cname.extents]
1217        } else {
1218            set extents 1
1219        }
1220        set _dim 3
1221        set _viewer flowvis
1222        set _comp2dims($cname) "3D"
1223        set _comp2unirect3d($cname) \
1224            [Rappture::Unirect3d \#auto $_xmlobj $_fldObj $cname $extents]
1225        set _comp2style($cname) [$_fldObj get $cname.style]
1226        if {[$_fldObj element $cname.flow] != ""} {
1227            set _comp2flowhints($cname) \
1228                [Rappture::FlowHints ::\#auto $_fldObj $cname $_units]
1229        }
1230        incr _counter
1231        return
1232    }
1233    if { $element == "unirect2d" && [$_fldObj element $cname.flow] != "" } {
1234        # Special case: unirect2d (normally deprecated) + flow.
1235        if { [$_fldObj element $cname.extents] != "" } {
1236            set extents [$_fldObj get $cname.extents]
1237        } else {
1238            set extents 1
1239        }
1240        set _dim 2
1241        set _viewer "flowvis"
1242        set _comp2dims($cname) "2D"
1243        set _comp2unirect2d($cname) \
1244            [Rappture::Unirect2d \#auto $_xmlobj $path]
1245        set _comp2style($cname) [$_fldObj get $cname.style]
1246        set _comp2flowhints($cname) \
1247            [Rappture::FlowHints ::\#auto $_fldObj $cname $_units]
1248        set _values [$_fldObj get $cname.values]
1249        set limits {}
1250        foreach axis { x y } {
1251            lappend limits $axis [$_comp2unirect2d($cname) limits $axis]
1252        }
1253        set xv [blt::vector create \#auto]
1254        $xv set $_values
1255        lappend limits "v" [$xv limits]
1256        blt::vector destroy $xv
1257        set _comp2limits($cname) $limits
1258        incr _counter
1259        return
1260    }
1261    set _viewer "contour"
1262    switch -- $element {
1263        "cloud" {
1264            set mesh [Rappture::Cloud::fetch $_xmlobj $path]
1265        }
1266        "mesh" {
1267            set mesh [Rappture::Mesh::fetch $_xmlobj $path]
1268        }           
1269        "unirect2d" {
1270            set mesh [Rappture::Unirect2d::fetch $_xmlobj $path]
1271            set _viewer "heightmap"
1272        }
1273    }
1274    set _dim [$mesh dimensions]
1275    if {$_dim == 1} {
1276        # Is this used anywhere?
1277        #
1278        # OOPS!  This is 1D data
1279        # Forget the cloud/field -- store BLT vectors
1280        #
1281        # Is there a natural growth path in generating output from 1D to
1282        # higher dimensions?  If there isn't, let's kill this in favor
1283        # or explicitly using a <curve> instead.  Otherwise, the features
1284        # (methods such as xmarkers) or the <curve> need to be added
1285        # to the <field>.
1286        #
1287        set xv [blt::vector create x$_counter]
1288        set yv [blt::vector create y$_counter]
1289       
1290        $yv set [$mesh points]
1291        $xv seq 0 1 [$yv length]
1292        # sort x-coords in increasing order
1293        $xv sort $yv
1294       
1295        set _comp2dims($cname) "1D"
1296        set _comp2xy($cname) [list $xv $yv]
1297        incr _counter
1298        return
1299    } elseif {$_dim == 2} {
1300        set _type "heightmap"
1301        set vector [blt::vector create \#auto]
1302        $vector set [$_fldObj get $cname.values]
1303        set _comp2dims($cname) "[$mesh dimensions]D"
1304        set _comp2mesh($cname) [list $mesh $vector]
1305        set _comp2style($cname) [$_fldObj get $cname.style]
1306        incr _counter
1307        array unset _comp2limits $cname
1308        lappend _comp2limits($cname) x [$mesh limits x]
1309        lappend _comp2limits($cname) y [$mesh limits y]
1310        lappend _comp2limits($cname) $label [$vector limits]
1311        lappend _comp2limits($cname) v [$vector limits]
1312        return
1313    } elseif {$_dim == 3} {
1314        #
1315        # 3D data: Store cloud/field as components
1316        #
1317        set values [$_fldObj get $cname.values]
1318        set farray [vtkFloatArray ::vals$_counter]
1319        foreach v $values {
1320            if {"" != $_units} {
1321                set v [Rappture::Units::convert $v \
1322                           -context $_units -to $_units -units off]
1323            }
1324            $farray InsertNextValue $v
1325        }
1326        set _viewer "isosurface"
1327        set _type "isosurface"
1328        set vector [blt::vector create \#auto]
1329        $vector set [$_fldObj get $cname.values]
1330        set _comp2dims($cname) "[$mesh dimensions]D"
1331        set _comp2mesh($cname) [list $mesh $vector]
1332        set _comp2style($cname) [$_fldObj get $cname.style]
1333        incr _counter
1334        lappend _comp2limits($cname) x [$mesh limits x]
1335        lappend _comp2limits($cname) y [$mesh limits y]
1336        lappend _comp2limits($cname) z [$mesh limits z]
1337        lappend _comp2limits($cname) $label [$vector limits]
1338        lappend _comp2limits($cname) v [$vector limits]
1339        return
1340    }
1341    error "unhandled case in field dim=$_dim element=$element"
1342}
1343
1344itcl::body Rappture::Field::AvsToVtk { comp contents } {
1345    package require vtk
1346
1347    set reader $this-datasetreader
1348    vtkAVSucdReader $reader
1349
1350    # Write the contents to a file just in case it's binary.
1351    set tmpfile file[pid].vtk
1352    set f [open "$tmpfile" "w"]
1353    fconfigure $f -translation binary -encoding binary
1354    puts $f $contents
1355    close $f
1356    $reader SetFileName $tmpfile
1357    $reader Update
1358    file delete $tmpfile
1359
1360    set output [$reader GetOutput]
1361    set pointData [$output GetPointData]
1362    set _scalars {}
1363    for { set i 0 } { $i < [$pointData GetNumberOfArrays] } { incr i } {
1364        set name [$pointData GetArrayName $i]
1365        lappend _scalars $name $name "???"
1366    }
1367    set writer $this-datasetwriter
1368    vtkDataSetWriter $writer
1369    $writer SetInputConnection [$reader GetOutputPort]
1370    $writer SetFileName $tmpfile
1371    $writer Write
1372    rename $reader ""
1373    rename $writer ""
1374    set f [open "$tmpfile" "r"]
1375    fconfigure $f -translation binary -encoding binary
1376    set vtkdata [read $f]
1377    close $f
1378    file delete $tmpfile
1379    return $vtkdata
1380}
Note: See TracBrowser for help on using the repository browser.