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

Last change on this file since 3721 was 3707, checked in by ldelgass, 11 years ago

Fix conversion of VTK 1D file to xy plot, fix some memory leaks and remove
unused code.

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