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

Last change on this file since 4482 was 4474, checked in by ldelgass, 10 years ago

Merge mesh/field fixes from trunk, add mesh viewer

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