source: branches/1.3/gui/scripts/field.tcl @ 4007

Last change on this file since 4007 was 3894, checked in by ldelgass, 11 years ago

Sync mergeinfo and merge field fix from trunk

File size: 54.5 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#  Vector data 2/3 streamlines
61#  Scalar data 1/
62#
63# Need <views> to properly do things like qdot: volume with a polydata
64# transparent shell.  The view will combine the two objects <field> and
65# <drawing> (??) into a single viewer.
66#
67package require Itcl
68package require BLT
69
70namespace eval Rappture {
71    # forward declaration
72}
73
74itcl::class Rappture::Field {
75    protected variable _dim 0;          # Dimension of the mesh
76    private variable _xmlobj "";        # ref to XML obj with field data
77    private variable _limits;           # maps axis name => {z0 z1} limits
78    private variable _field ""
79    private variable _comp2fldName ;    # cname => field names.
80    private variable _comp2type ;       # cname => type (e.g. "vectors")
81    private variable _comp2size ;       # cname => # of components in element
82    private variable _comp2assoc;       # cname => association (e.g. pointdata)
83    private variable _fld2Components;   # field name => number of components
84    private variable _fld2Label;        # field name => label
85    private variable _fld2Units;        # field name => units
86    private variable _hints
87    private variable _viewer "";        # Hints which viewer to use
88    private variable _xv "";            # For 1D meshes only.  Holds the points
89    private variable _isValid 0;        # Indicates if the field contains
90                                        # valid data.
91    private variable _isValidComponent; #  Array of valid components found
92    constructor {xmlobj path} {
93        # defined below
94    }
95    destructor {
96        # defined below
97    }
98    public method blob { cname }
99    public method components {args}
100    public method controls {option args}
101    public method extents {{cname -overall}}
102    public method numComponents {cname}
103    public method fieldlimits {}
104    public method flowhints { cname }
105    public method hints {{key ""}}
106    public method isunirect2d {}
107    public method isunirect3d {}
108    public method limits {axis}
109    public method mesh {{cname -overall}}
110    public method style { cname }
111    public method type {}
112    public method values { cname }
113    public method vtkdata {cname}
114
115    public method fieldnames { cname } {
116        if { ![info exists _comp2fldName($cname)] } {
117            return ""
118        }
119        return $_comp2fldName($cname)
120    }
121    public method fieldinfo { fname } {
122        lappend out $_fld2Label($fname)
123        lappend out $_fld2Units($fname)
124        lappend out $_fld2Components($fname)
125        return $out
126    }
127    public method isvalid {} {
128        return $_isValid
129    }
130    public method viewer {} {
131        return $_viewer
132    }
133    protected method Build {}
134    protected method _getValue {expr}
135
136    private variable _path "";          # Path of this object in the XML
137    private variable _units ""   ;      # system of units for this field
138    private variable _zmax 0     ;# length of the device
139
140    private variable _comp2dims  ;# maps component name => dimensionality
141    private variable _comp2xy    ;# maps component name => x,y vectors
142    private variable _comp2vtk   ;# maps component name => vtk file data
143    private variable _comp2dx    ;# maps component name => OpenDX data
144    private variable _comp2unirect2d ;# maps component name => unirect2d obj
145    private variable _comp2unirect3d ;# maps component name => unirect3d obj
146    private variable _comp2style ;# maps component name => style settings
147    private variable _comp2cntls ;# maps component name => x,y control points
148    private variable _comp2extents
149    private variable _comp2limits;      #  Array of limits per component
150    private variable _type ""
151    private variable _comp2flowhints
152    private variable _comp2mesh
153    private common _counter 0    ;# counter for unique vector names
154
155    private method AvsToVtk { cname contents }
156    private method BuildPointsOnMesh { cname }
157    private method ConvertToVtkData { cname }
158    protected method GetAssociation { cname }
159    protected method GetTypeAndSize { cname }
160    protected method ReadVtkDataSet { cname contents }
161
162    private method VerifyVtkDataSet { contents }
163    private variable _values ""
164}
165
166# ----------------------------------------------------------------------
167# CONSTRUCTOR
168# ----------------------------------------------------------------------
169itcl::body Rappture::Field::constructor {xmlobj path} {
170    package require vtk
171    if {![Rappture::library isvalid $xmlobj]} {
172        error "bad value \"$xmlobj\": should be Rappture::library"
173    }
174    set _xmlobj $xmlobj
175    set _path $path
176    set _field [$xmlobj element -as object $path]
177    set _units [$_field get units]
178
179    set xunits [$xmlobj get units]
180    if {"" == $xunits || "arbitrary" == $xunits} {
181        set xunits "um"
182    }
183
184    # determine the overall size of the device
185    set z0 [set z1 0]
186    foreach elem [$_xmlobj children components] {
187        switch -glob -- $elem {
188            box* {
189                if {![regexp {[0-9]$} $elem]} {
190                    set elem "${elem}0"
191                }
192                set z0 [$_xmlobj get components.$elem.corner0]
193                set z0 [Rappture::Units::convert $z0 \
194                    -context $xunits -to $xunits -units off]
195
196                set z1 [$_xmlobj get components.$elem.corner1]
197                set z1 [Rappture::Units::convert $z1 \
198                    -context $xunits -to $xunits -units off]
199
200                set _limits($elem) [list $z0 $z1]
201            }
202        }
203    }
204    set _zmax $z1
205
206    # build up vectors for various components of the field
207    Build
208}
209
210# ----------------------------------------------------------------------
211# DESTRUCTOR
212# ----------------------------------------------------------------------
213itcl::body Rappture::Field::destructor {} {
214    itcl::delete object $_field
215    # don't destroy the _xmlobj! we don't own it!
216
217    foreach name [array names _comp2xy] {
218        eval blt::vector destroy $_comp2xy($name)
219    }
220    foreach name [array names _comp2unirect2d] {
221        itcl::delete object $_comp2unirect2d($name)
222    }
223    foreach name [array names _comp2unirect3d] {
224        itcl::delete object $_comp2unirect3d($name)
225    }
226    foreach name [array names _comp2flowhints] {
227        itcl::delete object $_comp2flowhints($name)
228    }
229    foreach name [array names _comp2mesh] {
230        # Data is in the form of a mesh and a vector.
231        foreach { mesh vector } $_comp2mesh($name) break
232        # Release the mesh (may be shared)
233        set class [$mesh info class]
234        ${class}::release $mesh
235        # Destroy the vector
236        blt::vector destroy $vector
237    }
238}
239
240# ----------------------------------------------------------------------
241# USAGE: components ?-name|-dimensions|-style? ?<pattern>?
242#
243# Returns a list of names or types for the various components of
244# this field.  If the optional glob-style <pattern> is specified,
245# then it returns only the components with names matching the pattern.
246# ----------------------------------------------------------------------
247itcl::body Rappture::Field::components {args} {
248    Rappture::getopts args params {
249        flag what -name default
250        flag what -dimensions
251        flag what -style
252        flag what -particles
253        flag what -flow
254        flag what -box
255    }
256
257    set pattern *
258    if {[llength $args] > 0} {
259        set pattern [lindex $args 0]
260        set args [lrange $args 1 end]
261    }
262    if {[llength $args] > 0} {
263        error "wrong # args: should be \"components ?switches? ?pattern?\""
264    }
265
266    # There's only one dimension of the field.  Components can't have
267    # different dimensions in the same field.  They would by definition be
268    # using different meshes and viewers.
269    if { $params(what) == "-dimensions" } {
270        return "${_dim}D"
271    }
272    # BE CAREFUL: return component names in proper order
273    set rlist ""
274    set components {}
275    # First compile a list of valid components that match the pattern
276    foreach cname [$_field children -type component] {
277        if { ![info exists _isValidComponent($cname)] } {
278            continue
279        }
280        if { [string match $pattern $cname] } {
281            lappend components $cname
282        }
283    }
284    # Now handle the tests.
285    switch -- $params(what) {
286        -name {
287            set rlist $components
288        }
289        -style {
290            foreach cname $components {
291                if { [info exists _comp2style($cname)] } {
292                    lappend rlist $_comp2style($cname)
293                }
294            }
295        }
296    }
297    return $rlist
298}
299
300# ----------------------------------------------------------------------
301# USAGE: mesh ?<name>?
302#
303# Returns a list {xvec yvec} for the specified field component <name>.
304# If the name is not specified, then it returns the vectors for the
305# overall field (sum of all components).
306# ----------------------------------------------------------------------
307itcl::body Rappture::Field::mesh {{cname -overall}} {
308    if {$cname == "-overall" || $cname == "component0"} {
309        set cname [lindex [components -name] 0]
310    }
311    if {[info exists _comp2xy($cname)]} {
312        return [lindex $_comp2xy($cname) 0]  ;# return xv
313    }
314    if { [info exists _comp2vtk($cname)] } {
315        # FIXME: extract mesh from VTK file data.
316        if { $_comp2dims($cname) == "1D" } {
317            return $_xv
318        }
319        error "method \"mesh\" is not implemented for VTK file data"
320    }
321    if {[info exists _comp2dx($cname)]} {
322        return ""  ;# no mesh -- it's embedded in the value data
323    }
324    if {[info exists _comp2mesh($cname)]} {
325        return ""  ;# no mesh -- it's embedded in the value data
326    }
327    if {[info exists _comp2unirect2d($cname)]} {
328        set mobj [lindex $_comp2unirect2d($cname) 0]
329        return [$mobj mesh]
330    }
331    if {[info exists _comp2unirect3d($cname)]} {
332        set mobj [lindex $_comp2unirect3d($cname) 0]
333        return [$mobj mesh]
334    }
335    error "can't get field mesh: Unknown component \"$cname\": should be one of [join [lsort [array names _comp2dims]] {, }]"
336}
337
338# ----------------------------------------------------------------------
339# USAGE: values ?<name>?
340#
341# Returns a list {xvec yvec} for the specified field component <name>.
342# If the name is not specified, then it returns the vectors for the
343# overall field (sum of all components).
344# ----------------------------------------------------------------------
345itcl::body Rappture::Field::values {cname} {
346    if {$cname == "component0"} {
347        set cname "component"
348    }
349    if {[info exists _comp2xy($cname)]} {
350        return [lindex $_comp2xy($cname) 1]  ;# return yv
351    }
352    # VTK file data
353    if { [info exists _comp2vtk($cname)] } {
354        # FIXME: extract the values from the VTK file data
355        if { $_comp2dims($cname) == "1D" } {
356            return $_values
357        }
358        error "method \"values\" is not implemented for vtk file data"
359    }
360    # Points-on-mesh
361    if { [info exists _comp2mesh($cname)] } {
362        set vector [lindex $_comp2mesh($cname) 1]
363        return [$vector range 0 end]
364    }
365    if {[info exists _comp2dx($cname)]} {
366        return $_comp2dx($cname)  ;# return gzipped, base64-encoded DX data
367    }
368    if {[info exists _comp2unirect2d($cname)]} {
369        return [$_comp2unirect2d($cname) values]
370    }
371    if {[info exists _comp2unirect3d($cname)]} {
372        return [$_comp2unirect3d($cname) blob]
373    }
374    error "can't get field values. Unknown component \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
375}
376
377# ----------------------------------------------------------------------
378# USAGE: blob ?<name>?
379#
380# Returns a string representing the blob of data for the mesh and values.
381# ----------------------------------------------------------------------
382itcl::body Rappture::Field::blob {cname} {
383    if {$cname == "component0"} {
384        set cname "component"
385    }
386    if {[info exists _comp2xy($cname)]} {
387        return ""
388    }
389    if { [info exists _comp2vtk($cname)] } {
390        error "blob not implemented for VTK file data"
391    }
392    if {[info exists _comp2dx($cname)]} {
393        return $_comp2dx($cname)  ;# return gzipped, base64-encoded DX data
394    }
395    if {[info exists _comp2unirect2d($cname)]} {
396        set blob [$_comp2unirect2d($cname) blob]
397        lappend blob "values" $_values
398        return $blob
399    }
400    if {[info exists _comp2unirect3d($cname)]} {
401        return [$_comp2unirect3d($cname) blob]
402    }
403    error "can't get field blob: Unknown component \"$cname\": should be one of [join [lsort [array names _comp2dims]] {, }]"
404}
405
406# ----------------------------------------------------------------------
407# USAGE: limits <axis>
408#
409# Returns a list {min max} representing the limits for the specified
410# axis.
411# ----------------------------------------------------------------------
412itcl::body Rappture::Field::limits {which} {
413    set min ""
414    set max ""
415    blt::vector tmp zero
416
417    foreach cname [array names _comp2dims] {
418        switch -- $_comp2dims($cname) {
419            1D {
420                switch -- $which {
421                    x - xlin {
422                        set pos 0; set log 0; set axis x
423                    }
424                    xlog {
425                        set pos 0; set log 1; set axis x
426                    }
427                    y - ylin - v - vlin {
428                        set pos 1; set log 0; set axis y
429                    }
430                    ylog - vlog {
431                        set pos 1; set log 1; set axis y
432                    }
433                    default {
434                        error "bad axis \"$which\": should be x, xlin, xlog, y, ylin, ylog, v, vlin, vlog"
435                    }
436                }
437
438                set vname [lindex $_comp2xy($cname) $pos]
439                $vname variable vec
440
441                if {$log} {
442                    # on a log scale, use abs value and ignore 0's
443                    $vname dup tmp
444                    $vname dup zero
445                    zero expr {tmp == 0}            ;# find the 0's
446                    tmp expr {abs(tmp)}             ;# get the abs value
447                    tmp expr {tmp + zero*max(tmp)}  ;# replace 0's with abs max
448                    set axisMin [blt::vector expr min(tmp)]
449                    set axisMax [blt::vector expr max(tmp)]
450                } else {
451                    set axisMin $vec(min)
452                    set axisMax $vec(max)
453                }
454
455                if {"" == $min} {
456                    set min $axisMin
457                } elseif {$axisMin < $min} {
458                    set min $axisMin
459                }
460                if {"" == $max} {
461                    set max $axisMax
462                } elseif {$axisMax > $max} {
463                    set max $axisMax
464                }
465            }
466            2D - 3D {
467                if {[info exists _comp2unirect3d($cname)]} {
468                    set limits [$_comp2unirect3d($cname) limits $which]
469                    foreach {axisMin axisMax} $limits break
470                    set axis v
471                } elseif {[info exists _comp2limits($cname)]} {
472                    array set limits $_comp2limits($cname)
473                    switch -- $which {
474                        x - xlin - xlog {
475                            set axis x
476                            foreach {axisMin axisMax} $limits(x) break
477                        }
478                        y - ylin - ylog {
479                            set axis y
480                            foreach {axisMin axisMax} $limits(y) break
481                        }
482                        z - zlin - zlog {
483                            set axis z
484                            foreach {axisMin axisMax} $limits(z) break
485                        }
486                        v - vlin - vlog {
487                            set axis v
488                            foreach {axisMin axisMax} $limits(v) break
489                        }
490                        default {
491                            if { ![info exists limits($which)] } {
492                                error "limits: unknown axis \"$which\""
493                            }
494                            set axis v
495                            foreach {axisMin axisMax} $limits($which) break
496                        }
497                    }
498                } else {
499                    set axisMin 0  ;# HACK ALERT! must be OpenDX data
500                    set axisMax 1
501                    set axis v
502                }
503            }
504        }
505        if { "" == $min || $axisMin < $min } {
506            set min $axisMin
507        }
508        if { "" == $max || $axisMax > $max } {
509            set max $axisMax
510        }
511    }
512    blt::vector destroy tmp zero
513    set val [$_field get "${axis}axis.min"]
514    if {"" != $val && "" != $min} {
515        if {$val > $min} {
516            # tool specified this min -- don't go any lower
517            set min $val
518        }
519    }
520    set val [$_field get "${axis}axis.max"]
521    if {"" != $val && "" != $max} {
522        if {$val < $max} {
523            # tool specified this max -- don't go any higher
524            set max $val
525        }
526    }
527    return [list $min $max]
528}
529
530
531# ----------------------------------------------------------------------
532# USAGE: fieldlimits
533#
534# Returns a list {min max} representing the limits for the specified
535# axis.
536# ----------------------------------------------------------------------
537itcl::body Rappture::Field::fieldlimits {} {
538    foreach cname [array names _comp2limits] {
539        array set limits $_comp2limits($cname)
540        foreach fname $_comp2fldName($cname) {
541            if { ![info exists limits($fname)] } {
542                puts stderr "ERROR: field \"$fname\" unknown in \"$cname\""
543                continue
544            }
545            foreach {min max} $limits($fname) break
546            if { ![info exists overall($fname)] } {
547                set overall($fname) $limits($fname)
548                continue
549            }
550            foreach {omin omax} $overall($fname) break
551            if { $min < $omin } {
552                set omin $min
553            }
554            if { $max > $omax } {
555                set omax $max
556            }
557            set overall($fname) [list $min $max]
558        }
559    }
560    if { [info exists overall] } {
561        return [array get overall]
562    }
563    return ""
564}
565 
566# ----------------------------------------------------------------------
567# USAGE: controls get ?<name>?
568# USAGE: controls validate <path> <value>
569# USAGE: controls put <path> <value>
570#
571# Returns a list {path1 x1 y1 val1  path2 x2 y2 val2 ...} representing
572# control points for the specified field component <name>.
573# ----------------------------------------------------------------------
574itcl::body Rappture::Field::controls {option args} {
575    switch -- $option {
576        get {
577            set cname [lindex $args 0]
578            if {[info exists _comp2cntls($cname)]} {
579                return $_comp2cntls($cname)
580            }
581            return ""
582        }
583        validate {
584            set path [lindex $args 0]
585            set value [lindex $args 1]
586            set units [$_xmlobj get $path.units]
587
588            if {"" != $units} {
589                set nv [Rappture::Units::convert \
590                    $value -context $units -to $units -units off]
591            } else {
592                set nv $value
593            }
594            if {![string is double $nv]
595                  || [regexp -nocase {^(inf|nan)$} $nv]} {
596                error "Value out of range"
597            }
598
599            set rawmin [$_xmlobj get $path.min]
600            if {"" != $rawmin} {
601                set minv $rawmin
602                if {"" != $units} {
603                    set minv [Rappture::Units::convert \
604                        $minv -context $units -to $units -units off]
605                    set nv [Rappture::Units::convert \
606                        $value -context $units -to $units -units off]
607                }
608                # fix for the case when the user tries to
609                # compare values like minv=-500 nv=-0600
610                set nv [format "%g" $nv]
611                set minv [format "%g" $minv]
612
613                if {$nv < $minv} {
614                    error "Minimum value allowed here is $rawmin"
615                }
616            }
617
618            set rawmax [$_xmlobj get $path.max]
619            if {"" != $rawmax} {
620                set maxv $rawmax
621                if {"" != $units} {
622                    set maxv [Rappture::Units::convert \
623                        $maxv -context $units -to $units -units off]
624                    set nv [Rappture::Units::convert \
625                        $value -context $units -to $units -units off]
626                }
627                # fix for the case when the user tries to
628                # compare values like maxv=-500 nv=-0600
629                set nv [format "%g" $nv]
630                set maxv [format "%g" $maxv]
631
632                if {$nv > $maxv} {
633                    error "Maximum value allowed here is $rawmax"
634                }
635            }
636
637            return "ok"
638        }
639        put {
640            set path [lindex $args 0]
641            set value [lindex $args 1]
642            $_xmlobj put $path.current $value
643            Build
644        }
645        default {
646            error "bad field controls option \"$option\": should be get or put"
647        }
648    }
649}
650
651# ----------------------------------------------------------------------
652# USAGE: hints ?<keyword>?
653#
654# Returns a list of key/value pairs for various hints about plotting
655# this field.  If a particular <keyword> is specified, then it returns
656# the hint for that <keyword>, if it exists.
657# ----------------------------------------------------------------------
658itcl::body Rappture::Field::hints {{keyword ""}} {
659    if { ![info exists _hints] } {
660        foreach {key path} {
661            camera          camera.position
662            color           about.color
663            default         about.default
664            group           about.group
665            label           about.label
666            scale           about.scale
667            seeds           about.seeds
668            style           about.style
669            type            about.type
670            xlabel          about.xaxis.label
671            ylabel          about.yaxis.label
672            zlabel          about.zaxis.label
673            xunits          about.xaxis.units
674            yunits          about.yaxis.units
675            zunits          about.zaxis.units
676            units           units
677            updir           updir
678            vectors         about.vectors
679        } {
680            set str [$_field get $path]
681            if { "" != $str } {
682                set _hints($key) $str
683            }
684        }
685        foreach {key path} {
686            toolid          tool.id
687            toolname        tool.name
688            toolcommand     tool.execute
689            tooltitle       tool.title
690            toolrevision    tool.version.application.revision
691        } {
692            set str [$_xmlobj get $path]
693            if { "" != $str } {
694                set _hints($key) $str
695            }
696        }
697        # Set toolip and path hints
698        set _hints(path) $_path
699        if { [info exists _hints(group)] && [info exists _hints(label)] } {
700            # pop-up help for each curve
701            set _hints(tooltip) $_hints(label)
702        }
703    }
704    if { $keyword != "" } {
705        if {[info exists _hints($keyword)]} {
706            return $_hints($keyword)
707        }
708        return ""
709    }
710    return [array get _hints]
711}
712
713# ----------------------------------------------------------------------
714# USAGE: Build
715#
716# Used internally to build up the vector representation for the
717# field when the object is first constructed, or whenever the field
718# data changes.  Discards any existing vectors and builds everything
719# from scratch.
720# ----------------------------------------------------------------------
721itcl::body Rappture::Field::Build {} {
722
723    # Discard any existing data
724    foreach name [array names _comp2xy] {
725        eval blt::vector destroy $_comp2xy($name)
726    }
727    array unset _comp2vtk
728    foreach name [array names _comp2unirect2d] {
729        eval itcl::delete object $_comp2unirect2d($name)
730    }
731    foreach name [array names _comp2unirect3d] {
732        eval itcl::delete object $_comp2unirect3d($name)
733    }
734    catch {unset _comp2xy}
735    catch {unset _comp2dx}
736    catch {unset _comp2dims}
737    catch {unset _comp2style}
738    array unset _comp2unirect2d
739    array unset _comp2unirect3d
740    array unset _comp2extents
741    array unset _dataobj2type
742    #
743    # Scan through the components of the field and create
744    # vectors for each part.
745    #
746    array unset _isValidComponent
747    foreach cname [$_field children -type component] {
748        set type ""
749        if { ([$_field element $cname.constant] != "" &&
750              [$_field element $cname.domain] != "") ||
751              [$_field element $cname.xy] != "" } {
752            set type "1D"
753        } elseif { [$_field element $cname.mesh] != "" &&
754                   [$_field element $cname.values] != ""} {
755            set type "points-on-mesh"
756        } elseif { [$_field element $cname.vtk] != ""} {
757            set type "vtk"
758            set viewer [$_field get "about.view"]
759            if { $viewer != "" } {
760                set _viewer $viewer
761            }
762        } elseif {[$_field element $cname.opendx] != ""} {
763            global env
764            if { [info exists env(VTKVOLUME)] } {
765                set _viewer "vtkvolume"
766            }
767            set type "opendx"
768        } elseif {[$_field element $cname.dx] != ""} {
769            global env
770            if { [info exists env(VTKVOLUME)] } {
771                set _viewer "vtkvolume"
772            }
773            set type "dx"
774        } elseif {[$_field element $cname.ucd] != ""} {
775            set type "ucd"
776        }
777        set _comp2style($cname) ""
778        if { $type == "" } {
779            puts stderr "WARNING: ignoring field component \"$_path.$cname\": no data found."
780            continue
781        }
782        # Save the extents of the component
783        if { [$_field element $cname.extents] != "" } {
784            set extents [$_field get $cname.extents]
785        } else {
786            set extents 1
787        }
788        set _comp2extents($cname) $extents
789        set _type $type
790
791        GetTypeAndSize $cname
792        GetAssociation $cname
793        if { $_comp2size($cname) > 1 } {
794            set viewer [$_field get "about.view"]
795            if { $viewer == "" } {
796                set _viewer "glyphs"
797            }
798        }
799        if {$type == "1D"} {
800            #
801            # 1D data can be represented as 2 BLT vectors,
802            # one for x and the other for y.
803            #
804            set xv ""
805            set yv ""
806            set _dim 1
807            set val [$_field get $cname.constant]
808            if {$val != ""} {
809                set domain [$_field get $cname.domain]
810                if {$domain == "" || ![info exists _limits($domain)]} {
811                    set z0 0
812                    set z1 $_zmax
813                } else {
814                    foreach {z0 z1} $_limits($domain) { break }
815                }
816                set xv [blt::vector create x$_counter]
817                $xv append $z0 $z1
818
819                foreach {val pcomp} [_getValue $val] break
820                set yv [blt::vector create y$_counter]
821                $yv append $val $val
822
823                if {$pcomp != ""} {
824                    set zm [expr {0.5*($z0+$z1)}]
825                    set _comp2cntls($cname) \
826                        [list $pcomp $zm $val "$val$_units"]
827                }
828            } else {
829                set xydata [$_field get $cname.xy]
830                if {"" != $xydata} {
831                    set xv [blt::vector create x$_counter]
832                    set yv [blt::vector create y$_counter]
833                    set tmp [blt::vector create \#auto]
834                    $tmp set $xydata
835                    $tmp split $xv $yv
836                    blt::vector destroy $tmp
837                }
838            }
839
840            if {$xv != "" && $yv != ""} {
841                # sort x-coords in increasing order
842                $xv sort $yv
843                set _dim 1
844                set _comp2dims($cname) "1D"
845                set _comp2xy($cname) [list $xv $yv]
846                incr _counter
847            }
848        } elseif {$type == "points-on-mesh"} {
849            if { ![BuildPointsOnMesh $cname] } {
850                continue;               # Ignore this component
851            }
852        } elseif {$type == "vtk"} {
853            set contents [$_field get $cname.vtk]
854            if { $contents == "" } {
855                puts stderr "WARNING: no data fo \"$_path.$cname.vtk\""
856                continue;               # Ignore this component
857            }
858            ReadVtkDataSet $cname $contents
859            set _comp2vtk($cname) $contents
860            set _comp2style($cname) [$_field get $cname.style]
861            incr _counter
862        } elseif {$type == "dx" || $type == "opendx" } {
863            #
864            # HACK ALERT!  Extract gzipped, base64-encoded OpenDX
865            # data.  Assume that it's 3D.  Pass it straight
866            # off to the NanoVis visualizer.
867            #
868            set viewer [$_field get "about.view"]
869            if { $viewer != "" } {
870                set _viewer $viewer
871            }
872            if { $_viewer == "" } {
873                set _viewer "nanovis"
874            }
875            set _dim 3
876            set _comp2dims($cname) "3D"
877            if { $_viewer != "nanovis" } {
878                set vtkdata  [$_field get -decode yes $cname.$type]
879                if { $vtkdata == "" } {
880                    puts stderr "WARNING: no data for \"$_path.$cname.$type\""
881                    continue;               # Ignore this component
882                }
883                if 0 {
884                    set f [open /tmp/$_path.$cname.dx "w"]
885                    puts -nonewline $f $vtkdata
886                    close $f
887                }
888                set vtkdata  [Rappture::DxToVtk $vtkdata]
889                if 0 {
890                    set f [open /tmp/$_path.$cname.vtk "w"]
891                    puts -nonewline $f $vtkdata
892                    close $f
893                }
894                ReadVtkDataSet $cname $vtkdata
895                set _type "vtk"
896                set _comp2vtk($cname) $vtkdata
897            } else {
898                set contents [$_field get -decode no $cname.$type]
899                if { $contents == "" } {
900                    puts stderr "WARNING: no data for \"$_path.$cname.$type\""
901                    continue;               # Ignore this component
902                }
903                set _type "dx"
904                set _comp2dx($cname) $contents
905            }
906            set _comp2style($cname) [$_field get $cname.style]
907            if {[$_field element $cname.flow] != ""} {
908                set _comp2flowhints($cname) \
909                    [Rappture::FlowHints ::\#auto $_field $cname $_units]
910            }
911            incr _counter
912        } elseif { $type == "ucd"} {
913            set contents [$_field get $cname.ucd]
914            if { $contents == "" } {
915                continue;               # Ignore this compoennt
916            }
917            set vtkdata [AvsToVtk $cname $contents]
918            ReadVtkDataSet $cname $vtkdata
919            set _comp2vtk($cname) $vtkdata
920            set _comp2style($cname) [$_field get $cname.style]
921            incr _counter
922        }
923        set _isValidComponent($cname) 1
924    }
925    if { [array size _isValidComponent] == 0 } {
926        puts stderr "WARNING: no valid components for field \"$_path\""
927        return 0
928    }
929    # Sanity check.  Verify that all components of the field have the same
930    # dimension.
931    set dim ""
932    foreach cname [array names _comp2dims] {
933        if { $dim == "" } {
934            set dim $_comp2dims($cname)
935            continue
936        }
937        if { $dim != $_comp2dims($cname) } {
938            puts stderr "WARNING: field can't have components of different dimensions: [join [array get _comp2dims] ,]"
939            return 0
940        }
941    }
942    # FIXME: about.scalars and about.vectors are temporary.  With views
943    #        the label and units for each field will be specified there.
944    #
945    # FIXME: Test that every <field><component> has the same field names,
946    #        units, components.
947    #
948    # Override what we found in the VTK file with names that the user
949    # selected.  We override the field label and units.
950    foreach { fname label units } [$_field get about.scalars] {
951        if { ![info exists _fld2Name($fname)] } {
952            set _fld2Name($fname) $fname
953            set _fld2Components($fname) 1
954        }
955        set _fld2Label($fname) $label
956        set _fld2Units($fname) $units
957    }
958    foreach { fname label units } [$_field get about.vectors] {
959        if { ![info exists _fld2Name($fname)] } {
960            set _fld2Name($fname) $fname
961            # We're just marking the field as vector (> 1) for now.
962            set _fld2Components($fname) 3
963        }
964        set _fld2Label($fname) $label
965        set _fld2Units($fname) $units
966    }
967    set _isValid 1
968    return 1
969}
970
971# ----------------------------------------------------------------------
972# USAGE: _getValue <expr>
973#
974# Used internally to get the value for an expression <expr>.  Returns
975# a list of the form {val parameterPath}, where val is the numeric
976# value of the expression, and parameterPath is the XML path to the
977# parameter representing the value, or "" if the <expr> does not
978# depend on any parameters.
979# ----------------------------------------------------------------------
980itcl::body Rappture::Field::_getValue {expr} {
981    #
982    # First, look for the expression among the <parameter>'s
983    # associated with the device.
984    #
985    set found 0
986    foreach pcomp [$_xmlobj children parameters] {
987        set id [$_xmlobj element -as id parameters.$pcomp]
988        if {[string equal $id $expr]} {
989            set val [$_xmlobj get parameters.$pcomp.current]
990            if {"" == $val} {
991                set val [$_xmlobj get parameters.$pcomp.default]
992            }
993            if {"" != $val} {
994                set expr $val
995                set found 1
996                break
997            }
998        }
999    }
1000    if {$found} {
1001        set pcomp "parameters.$pcomp"
1002    } else {
1003        set pcomp ""
1004    }
1005
1006    if {$_units != ""} {
1007        set expr [Rappture::Units::convert $expr \
1008            -context $_units -to $_units -units off]
1009    }
1010
1011    return [list $expr $pcomp]
1012}
1013
1014#
1015# isunirect2d  --
1016#
1017# Returns if the field is a unirect2d object. 
1018#
1019itcl::body Rappture::Field::isunirect2d { } {
1020    return [expr [array size _comp2unirect2d] > 0]
1021}
1022
1023#
1024# isunirect3d  --
1025#
1026# Returns if the field is a unirect3d object. 
1027#
1028itcl::body Rappture::Field::isunirect3d { } {
1029    return [expr [array size _comp2unirect3d] > 0]
1030}
1031
1032#
1033# flowhints  --
1034#
1035# Returns the hints associated with a flow vector field. 
1036#
1037itcl::body Rappture::Field::flowhints { cname } {
1038    if { [info exists _comp2flowhints($cname)] } {
1039        return $_comp2flowhints($cname)
1040    }
1041    return ""
1042}
1043
1044#
1045# style  --
1046#
1047# Returns the style associated with a component of the field. 
1048#
1049itcl::body Rappture::Field::style { cname } {
1050    if { [info exists _comp2style($cname)] } {
1051        return $_comp2style($cname)
1052    }
1053    return ""
1054}
1055
1056#
1057# type  --
1058#
1059# Returns the style associated with a component of the field. 
1060#
1061itcl::body Rappture::Field::type {} {
1062    return $_type
1063}
1064
1065#
1066# numComponents --
1067#
1068# Returns if the number of components in the field component.
1069#
1070itcl::body Rappture::Field::numComponents {cname} {
1071    set name $cname
1072    regsub -all { } $name {_} name
1073    if {[info exists _fld2Components($name)] } {
1074        return $_fld2Components($name)
1075    }
1076    return 1;                           # Default to scalar.
1077}
1078
1079#
1080# extents --
1081#
1082# Returns if the field is a unirect2d object. 
1083#
1084itcl::body Rappture::Field::extents {{cname -overall}} {
1085    if {$cname == "-overall" } {
1086        set max 0
1087        foreach cname [$_field children -type component] {
1088            if { ![info exists _comp2unirect3d($cname)] &&
1089                 ![info exists _comp2extents($cname)] } {
1090                continue
1091            }
1092            set value $_comp2extents($cname)
1093            if { $max < $value } {
1094                set max $value
1095            }
1096        }
1097        return $max
1098    }
1099    if { $cname == "component0"} {
1100        set cname [lindex [components -name] 0]
1101    }
1102    return $_comp2extents($cname)
1103}
1104
1105itcl::body Rappture::Field::ConvertToVtkData { cname } {
1106    set ds ""
1107    switch -- [typeof $cname] {
1108        "unirect2d" {
1109            foreach { x1 x2 xN y1 y2 yN } [$dataobj mesh $cname] break
1110            set spacingX [expr {double($x2 - $x1)/double($xN - 1)}]
1111            set spacingY [expr {double($y2 - $y1)/double($yN - 1)}]
1112           
1113            set ds [vtkImageData $this-grdataTemp]
1114            $ds SetDimensions $xN $yN 1
1115            $ds SetOrigin $x1 $y1 0
1116            $ds SetSpacing $spacingX $spacingY 0
1117            set arr [vtkDoubleArray $this-arrTemp]
1118            foreach {val} [$dataobj values $cname] {
1119                $arr InsertNextValue $val
1120            }
1121            [$ds GetPointData] SetScalars $arr
1122        }
1123        "unirect3d" {
1124            foreach { x1 x2 xN y1 y2 yN z1 z2 zN } [$dataobj mesh $cname] break
1125            set spacingX [expr {double($x2 - $x1)/double($xN - 1)}]
1126            set spacingY [expr {double($y2 - $y1)/double($yN - 1)}]
1127            set spacingZ [expr {double($z2 - $z1)/double($zN - 1)}]
1128           
1129            set ds [vtkImageData $this-grdataTemp]
1130            $ds SetDimensions $xN $yN $zN
1131            $ds SetOrigin $x1 $y1 $z1
1132            $ds SetSpacing $spacingX $spacingY $spacingZ
1133            set arr [vtkDoubleArray $this-arrTemp]
1134            foreach {val} [$dataobj values $cname] {
1135                $arr InsertNextValue $val
1136            }
1137            [$ds GetPointData] SetScalars $val
1138        }
1139        "contour" {
1140            return [$dataobj blob $cname]
1141        }
1142        "dx" {
1143            return [Rappture::DxToVtk $_comp2dx($cname)]
1144        }
1145        default {
1146            set mesh [$dataobj mesh $cname]
1147            switch -- [$mesh GetClassName] {
1148                vtkPoints {
1149                    # handle cloud of points
1150                    set ds [vtkPolyData $this-polydataTemp]
1151                    $ds SetPoints $mesh
1152                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1153                }
1154                vtkPolyData {
1155                    set ds [vtkPolyData $this-polydataTemp]
1156                    $ds ShallowCopy $mesh
1157                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1158                }
1159                vtkUnstructuredGrid {
1160                    # handle 3D grid with connectivity
1161                    set ds [vtkUnstructuredGrid $this-grdataTemp]
1162                    $ds ShallowCopy $mesh
1163                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1164                }
1165                vtkRectilinearGrid {
1166                    # handle 3D grid with connectivity
1167                    set ds [vtkRectilinearGrid $this-grdataTemp]
1168                    $ds ShallowCopy $mesh
1169                    [$ds GetPointData] SetScalars [$dataobj values $cname]
1170                }
1171                default {
1172                    error "don't know how to handle [$mesh GetClassName] data"
1173                }
1174            }
1175        }
1176    }
1177
1178    if {"" != $ds} {
1179        set writer [vtkDataSetWriter $this-dsWriterTmp]
1180        $writer SetInput $ds
1181        $writer SetFileTypeToASCII
1182        $writer WriteToOutputStringOn
1183        $writer Write
1184        set out [$writer GetOutputString]
1185        $ds Delete
1186        $writer Delete
1187    } else {
1188        set out ""
1189        error "No DataSet to write"
1190    }
1191
1192    append out "\n"
1193    return $out
1194}
1195
1196itcl::body Rappture::Field::VerifyVtkDataSet { contents } {
1197    package require vtk
1198
1199    set reader $this-datasetreader
1200    vtkDataSetReader $reader
1201
1202    # Write the contents to a file just in case it's binary.
1203    set tmpfile file[pid].vtk
1204    set f [open "$tmpfile" "w"]
1205    fconfigure $f -translation binary -encoding binary
1206    puts $f $contents
1207    close $f
1208
1209    $reader SetFileName $tmpfile
1210    $reader ReadAllNormalsOn
1211    $reader ReadAllTCoordsOn
1212    $reader ReadAllScalarsOn
1213    $reader ReadAllColorScalarsOn
1214    $reader ReadAllVectorsOn
1215    $reader ReadAllTensorsOn
1216    $reader ReadAllFieldsOn
1217    $reader Update
1218    file delete $tmpfile
1219
1220    set dataset [$reader GetOutput]
1221    set dataAttrs [$dataset GetPointData]
1222    if { $dataAttrs == ""} {
1223        puts stderr "WARNING: no point data found in \"$_path\""
1224        rename $reader ""
1225        return 0
1226    }
1227    rename $reader ""
1228}
1229
1230itcl::body Rappture::Field::ReadVtkDataSet { cname contents } {
1231    package require vtk
1232
1233    set reader $this-datasetreader
1234    vtkDataSetReader $reader
1235
1236    # Write the contents to a file just in case it's binary.
1237    set tmpfile file[pid].vtk
1238    set f [open "$tmpfile" "w"]
1239    fconfigure $f -translation binary -encoding binary
1240    puts $f $contents
1241    close $f
1242
1243    $reader SetFileName $tmpfile
1244    $reader ReadAllNormalsOn
1245    $reader ReadAllTCoordsOn
1246    $reader ReadAllScalarsOn
1247    $reader ReadAllColorScalarsOn
1248    $reader ReadAllVectorsOn
1249    $reader ReadAllTensorsOn
1250    $reader ReadAllFieldsOn
1251    $reader Update
1252    file delete $tmpfile
1253
1254    set dataset [$reader GetOutput]
1255    set limits {}
1256    foreach {xmin xmax ymin ymax zmin zmax} [$dataset GetBounds] break
1257    # Figure out the dimension of the mesh from the bounds.
1258    set _dim 0
1259    if { $xmax > $xmin } {
1260        incr _dim
1261    }
1262    if { $ymax > $ymin } {
1263        incr _dim
1264    }
1265    if { $zmax > $zmin } {
1266        incr _dim
1267    }
1268    if { $_viewer == "" } {
1269        if { $_dim == 2 } {
1270            set _viewer contour
1271        } else {
1272            set _viewer isosurface
1273        }
1274    }
1275    set _comp2dims($cname) ${_dim}D
1276    if { $_dim < 2 } {
1277        set numPoints [$dataset GetNumberOfPoints]
1278        set xv [blt::vector create \#auto]
1279        for { set i 0 } { $i < $numPoints } { incr i } {
1280            set point [$dataset GetPoint $i]
1281            $xv append [lindex $point 0]
1282        }
1283        set yv [blt::vector create \#auto]
1284        set dataAttrs [$dataset GetPointData]
1285        if { $dataAttrs == ""} {
1286            puts stderr "WARNING: no point data found in \"$_path\""
1287            rename $reader ""
1288            return 0
1289        }
1290        set array [$dataAttrs GetScalars]
1291        if { $array == ""} {
1292            puts stderr "WARNING: no scalar point data found in \"$_path\""
1293            rename $reader ""
1294            return 0
1295        }
1296        set numTuples [$array GetNumberOfTuples]
1297        for { set i 0 } { $i < $numTuples } { incr i } {
1298            $yv append [$array GetComponent $i 0]
1299        }
1300        $xv sort $yv
1301        set _comp2xy($cname) [list $xv $yv]
1302    }
1303    lappend limits x [list $xmin $xmax]
1304    lappend limits y [list $ymin $ymax]
1305    lappend limits z [list $zmin $zmax]
1306    set dataAttrs [$dataset GetPointData]
1307    if { $dataAttrs == ""} {
1308        puts stderr "WARNING: no point data found in \"$_path\""
1309        rename $reader ""
1310        return 0
1311    }
1312    set vmin 0
1313    set vmax 1
1314    set numArrays [$dataAttrs GetNumberOfArrays]
1315    if { $numArrays > 0 } {
1316        set array [$dataAttrs GetArray 0]
1317        # Calling GetRange with component set to -1 will return
1318        # either the scalar range or vector magnitude range
1319        foreach {vmin vmax} [$array GetRange -1] break
1320
1321        for {set i 0} {$i < [$dataAttrs GetNumberOfArrays] } {incr i} {
1322            set array [$dataAttrs GetArray $i]
1323            set fname  [$dataAttrs GetArrayName $i]
1324            foreach {min max} [$array GetRange -1] break
1325            lappend limits $fname [list $min $max]
1326            set _fld2Units($fname) ""
1327            set _fld2Label($fname) $fname
1328            # Let the VTK file override the <type> designated.
1329            set _fld2Components($fname) [$array GetNumberOfComponents]
1330            lappend _comp2fldName($cname) $fname
1331        }
1332    }
1333   
1334    lappend limits v [list $vmin $vmax]
1335    set _comp2limits($cname) $limits
1336
1337    rename $reader ""
1338}
1339
1340#
1341# vtkdata --
1342#
1343#       Returns a string representing the mesh and field data for a specific
1344#       component in the legacy VTK file format.
1345#
1346itcl::body Rappture::Field::vtkdata {cname} {
1347    if {$cname == "component0"} {
1348        set cname "component"
1349    }
1350    # DX: Convert DX to VTK
1351    if {[info exists _comp2dx($cname)]} {
1352        set data $_comp2dx($cname)
1353        set data [Rappture::encoding::decode $data]
1354        return [Rappture::DxToVtk $data]
1355    }
1356    # Unirect3d: isosurface
1357    if {[info exists _comp2unirect3d($cname)]} {
1358        return [$_comp2unirect3d($cname) vtkdata]
1359    }
1360    # VTK file data:
1361    if { [info exists _comp2vtk($cname)] } {
1362        return $_comp2vtk($cname)
1363    }
1364    # Points on mesh:  Construct VTK file output.
1365    if { [info exists _comp2mesh($cname)] } {
1366        # Data is in the form mesh and vector
1367        foreach {mesh vector} $_comp2mesh($cname) break
1368        set label $cname
1369        regsub -all { } $label {_} label
1370        append out "# vtk DataFile Version 3.0\n"
1371        append out "[hints label]\n"
1372        append out "ASCII\n"
1373        append out [$mesh vtkdata]
1374
1375        if { $_comp2assoc($cname) == "pointdata" } {
1376            set vtkassoc "POINT_DATA"
1377        } elseif { $_comp2assoc($cname) == "celldata" } {
1378            set vtkassoc "CELL_DATA"
1379        } elseif { $_comp2assoc($cname) == "fielddata" } {
1380            set vtkassoc "FIELD"
1381        } else {
1382            error "unknown association \"$_comp2assoc($cname)\""
1383        }
1384        set elemSize [numComponents $cname]
1385        set numValues [expr [$vector length] / $elemSize]
1386        if { $_comp2assoc($cname) == "fielddata" } {
1387            append out "$vtkassoc FieldData 1\n"
1388            append out "$label $elemSize $numValues double\n"
1389        } else {
1390            append out "$vtkassoc $numValues\n"
1391            if { $_comp2type($cname) == "colorscalars" } {
1392                # Must be float for ASCII, unsigned char for BINARY
1393                append out "COLOR_SCALARS $label $elemSize\n"
1394            } elseif { $_comp2type($cname) == "normals" } {
1395                # elemSize must equal 3
1396                append out "NORMALS $label double\n"
1397            } elseif { $_comp2type($cname) == "scalars" } {
1398                # elemSize can be 1, 2, 3 or 4
1399                append out "SCALARS $label double $elemSize\n"
1400                append out "LOOKUP_TABLE default\n"
1401            } elseif { $_comp2type($cname) == "tcoords" } {
1402                # elemSize must be 1, 2, or 3
1403                append out "TEXTURE_COORDINATES $label $elemSize double\n"
1404            } elseif { $_comp2type($cname) == "tensors" } {
1405                # elemSize must equal 9
1406                append out "TENSORS $label double\n"
1407            } elseif { $_comp2type($cname) == "vectors" } {
1408                # elemSize must equal 3
1409                append out "VECTORS $label double\n"
1410            } else {
1411                error "unknown element type \"$_comp2type($cname)\""
1412            }
1413        }
1414        append out [$vector range 0 end]
1415        append out "\n"
1416        if 0 {
1417            VerifyVtkDataSet $out
1418        }
1419        return $out
1420    }
1421    error "can't find vtkdata for $cname. This method should only be called by the vtkheightmap widget"
1422}
1423
1424#
1425# BuildPointsOnMesh --
1426#
1427#       Parses the field XML description to build a mesh and values vector
1428#       representing the field.  Right now we handle the deprecated types
1429#       of "cloud", "unirect2d", and "unirect3d" (mostly for flows).
1430#
1431itcl::body Rappture::Field::BuildPointsOnMesh {cname} {
1432    #
1433    # More complex 2D/3D data is represented by a mesh
1434    # object and an associated vector for field values.
1435    #
1436    set path [$_field get $cname.mesh]
1437    if {[$_xmlobj element $path] == ""} {
1438        # Unknown mesh designated.
1439        return 0
1440    }
1441    set viewer [$_field get "about.view"]
1442    if { $viewer != "" } {
1443        set _viewer $viewer
1444    }
1445    set element [$_xmlobj element -as type $path]
1446    set name $cname
1447    regsub -all { } $name {_} name
1448    set _fld2Label($name) $name
1449    set label [hints zlabel]
1450    if { $label != "" } {
1451        set _fld2Label($name) $label
1452    }
1453    set _fld2Units($name) [hints zunits]
1454    set _fld2Components($name) $_comp2size($cname)
1455    lappend _comp2fldName($cname) $name
1456
1457    # Handle bizarre cases that hopefully will be deprecated.
1458    if { $element == "unirect3d" } {
1459        # Special case: unirect3d (should be deprecated) + flow.
1460        if { [$_field element $cname.extents] != "" } {
1461            set vectorsize [$_field get $cname.extents]
1462        } else {
1463            set vectorsize 1
1464        }
1465        set _dim 3
1466        if { $_viewer == "" } {
1467            set _viewer flowvis
1468        }
1469        set _comp2dims($cname) "3D"
1470        set _comp2unirect3d($cname) \
1471            [Rappture::Unirect3d \#auto $_xmlobj $_field $cname $vectorsize]
1472        set _comp2style($cname) [$_field get $cname.style]
1473        if {[$_field element $cname.flow] != ""} {
1474            set _comp2flowhints($cname) \
1475                [Rappture::FlowHints ::\#auto $_field $cname $_units]
1476        }
1477        incr _counter
1478        return 1
1479    }
1480    if { $element == "unirect2d" && [$_field element $cname.flow] != "" } {
1481        # Special case: unirect2d (normally deprecated) + flow.
1482        if { [$_field element $cname.extents] != "" } {
1483            set vectorsize [$_field get $cname.extents]
1484        } else {
1485            set vectorsize 1
1486        }
1487        set _dim 2
1488        if { $_viewer == "" } {
1489            set _viewer "flowvis"
1490        }
1491        set _comp2dims($cname) "2D"
1492        set _comp2unirect2d($cname) \
1493            [Rappture::Unirect2d \#auto $_xmlobj $path]
1494        set _comp2style($cname) [$_field get $cname.style]
1495        set _comp2flowhints($cname) \
1496            [Rappture::FlowHints ::\#auto $_field $cname $_units]
1497        set _values [$_field get $cname.values]
1498        set limits {}
1499        foreach axis { x y } {
1500            lappend limits $axis [$_comp2unirect2d($cname) limits $axis]
1501        }
1502        set xv [blt::vector create \#auto]
1503        $xv set $_values
1504        lappend limits $cname [$xv limits]
1505        lappend limits v [$xv limits]
1506        blt::vector destroy $xv
1507        set _comp2limits($cname) $limits
1508        incr _counter
1509        return 1
1510    }
1511    switch -- $element {
1512        "cloud" {
1513            set mesh [Rappture::Cloud::fetch $_xmlobj $path]
1514        }
1515        "mesh" {
1516            set mesh [Rappture::Mesh::fetch $_xmlobj $path]
1517        }           
1518        "unirect2d" {
1519            if { $_viewer == "" } {
1520                set _viewer "heightmap"
1521            }
1522            set mesh [Rappture::Unirect2d::fetch $_xmlobj $path]
1523        }
1524    }
1525    if { ![$mesh isvalid] } {
1526        puts stderr "Mesh is invalid"
1527        return 0
1528    }
1529    set _dim [$mesh dimensions]
1530    if { $_dim == 3 } {
1531        set dim 0
1532        foreach axis {x y z} {
1533            foreach {min max} [$mesh limits $axis] {
1534                if { $min < $max } {
1535                    incr dim
1536                }
1537            }
1538        }
1539        if { $dim  != 3 } {
1540            set _dim $dim
1541        }
1542    }
1543       
1544    if {$_dim == 1} {
1545        # 1D data: Create vectors for graph widget.
1546        # Is this used anywhere?
1547        #
1548        # OOPS!  This is 1D data
1549        # Forget the cloud/field -- store BLT vectors
1550        #
1551        # Is there a natural growth path in generating output from 1D to
1552        # higher dimensions?  If there isn't, let's kill this in favor
1553        # or explicitly using a <curve> instead.  Otherwise, the features
1554        # (methods such as xmarkers) or the <curve> need to be added
1555        # to the <field>.
1556        #
1557        set xv [blt::vector create x$_counter]
1558        set yv [blt::vector create y$_counter]
1559       
1560        $yv set [$mesh points]
1561        $xv seq 0 1 [$yv length]
1562        # sort x-coords in increasing order
1563        $xv sort $yv
1564       
1565        set _comp2dims($cname) "1D"
1566        set _comp2xy($cname) [list $xv $yv]
1567        incr _counter
1568        return 1
1569    }
1570    if {$_dim == 2} {
1571        # 2D data: By default surface or contour plot using heightmap widget.
1572        set _type "heightmap"
1573        set v [blt::vector create \#auto]
1574        $v set [$_field get $cname.values]
1575        if { [$v length] == 0 } {
1576            return 0
1577        }
1578        if { $_viewer == "" } {
1579            set _viewer "contour"
1580        }
1581        set _comp2dims($cname) "[$mesh dimensions]D"
1582        set _comp2mesh($cname) [list $mesh $v]
1583        set _comp2style($cname) [$_field get $cname.style]
1584        incr _counter
1585        array unset _comp2limits $cname
1586        lappend _comp2limits($cname) x [$mesh limits x]
1587        lappend _comp2limits($cname) y [$mesh limits y]
1588        lappend _comp2limits($cname) $cname [$v limits]
1589        lappend _comp2limits($cname) v [$v limits]
1590        return 1
1591    }
1592    if {$_dim == 3} {
1593        # 3D data: By default isosurfaces plot using isosurface widget.
1594        if { $_viewer == "" } {
1595            set _viewer "isosurface"
1596        }
1597        set _type "isosurface"
1598        set v [blt::vector create \#auto]
1599        $v set [$_field get $cname.values]
1600        if { [$v length] == 0 } {
1601            return 0
1602        }
1603        set _comp2dims($cname) "[$mesh dimensions]D"
1604        set _comp2mesh($cname) [list $mesh $v]
1605        set _comp2style($cname) [$_field get $cname.style]
1606        incr _counter
1607        lappend _comp2limits($cname) x [$mesh limits x]
1608        lappend _comp2limits($cname) y [$mesh limits y]
1609        lappend _comp2limits($cname) z [$mesh limits z]
1610        lappend _comp2limits($cname) $cname [$v limits]
1611        lappend _comp2limits($cname) v [$v limits]
1612        return 1
1613    }
1614    error "unhandled case in field dim=$_dim element=$element"
1615}
1616
1617itcl::body Rappture::Field::AvsToVtk { cname contents } {
1618    package require vtk
1619
1620    set reader $this-datasetreader
1621    vtkAVSucdReader $reader
1622
1623    # Write the contents to a file just in case it's binary.
1624    set tmpfile $cname[pid].ucd
1625    set f [open "$tmpfile" "w"]
1626    fconfigure $f -translation binary -encoding binary
1627    puts $f $contents
1628    close $f
1629    $reader SetFileName $tmpfile
1630    $reader Update
1631    file delete $tmpfile
1632
1633    set tmpfile $cname[pid].vtk
1634    set writer $this-datasetwriter
1635    vtkDataSetWriter $writer
1636    $writer SetInputConnection [$reader GetOutputPort]
1637    $writer SetFileName $tmpfile
1638    $writer Write
1639    rename $reader ""
1640    rename $writer ""
1641
1642    set f [open "$tmpfile" "r"]
1643    fconfigure $f -translation binary -encoding binary
1644    set vtkdata [read $f]
1645    close $f
1646    file delete $tmpfile
1647    return $vtkdata
1648}
1649
1650itcl::body Rappture::Field::GetTypeAndSize { cname } {
1651    array set type2components {
1652        "colorscalars"         4
1653        "normals"              3
1654        "scalars"              1
1655        "tcoords"              2
1656        "tensors"              9
1657        "vectors"              3
1658    }
1659    set type [$_field get $cname.elemtype]
1660    if { $type == "" } {
1661        set type "scalars"
1662    }
1663    if { ![info exists type2components($type)] } {
1664        error "unknown <elemtype> \"$type\" in field"
1665    }
1666    set size [$_field get $cname.elemsize]
1667    if { $size == "" } {
1668        set size $type2components($type)
1669    }
1670    set _comp2type($cname) $type
1671    set _comp2size($cname) $size
1672}
1673
1674itcl::body Rappture::Field::GetAssociation { cname } {
1675    set assoc [$_field get $cname.association]
1676    if { $assoc == "" } {
1677        set _comp2assoc($cname) "pointdata"
1678        return
1679    }
1680    switch -- $assoc {
1681        "pointdata" - "celldata" - "fielddata" {
1682            set _comp2assoc($cname) $assoc
1683            return
1684        }
1685        default {
1686            error "unknown field association \"$assoc\""
1687        }
1688    }
1689}
Note: See TracBrowser for help on using the repository browser.