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

Last change on this file since 4406 was 4387, checked in by gah, 10 years ago

add check for number of field points

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