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

Last change on this file since 4490 was 4490, checked in by ldelgass, 7 years ago

merge r4489 from trunk

File size: 54.9 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        error "method \"values\" is not implemented for dx file 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# USAGE: fieldlimits
528#
529# Returns a list {min max} representing the limits for the specified
530# axis.
531# ----------------------------------------------------------------------
532itcl::body Rappture::Field::fieldlimits {} {
533    foreach cname [array names _comp2limits] {
534        array set limits $_comp2limits($cname)
535        foreach fname [fieldnames $cname] {
536            if { ![info exists limits($fname)] } {
537                puts stderr "ERROR: field \"$fname\" unknown in \"$cname\""
538                continue
539            }
540            foreach {min max} $limits($fname) break
541            if { ![info exists overall($fname)] } {
542                set overall($fname) $limits($fname)
543                continue
544            }
545            foreach {omin omax} $overall($fname) break
546            if { $min < $omin } {
547                set omin $min
548            }
549            if { $max > $omax } {
550                set omax $max
551            }
552            set overall($fname) [list $min $max]
553        }
554    }
555    if { [info exists overall] } {
556        return [array get overall]
557    }
558    return ""
559}
560 
561# ----------------------------------------------------------------------
562# USAGE: controls get ?<name>?
563# USAGE: controls validate <path> <value>
564# USAGE: controls put <path> <value>
565#
566# Returns a list {path1 x1 y1 val1  path2 x2 y2 val2 ...} representing
567# control points for the specified field component <name>.
568# ----------------------------------------------------------------------
569itcl::body Rappture::Field::controls {option args} {
570    switch -- $option {
571        get {
572            set cname [lindex $args 0]
573            if {[info exists _comp2cntls($cname)]} {
574                return $_comp2cntls($cname)
575            }
576            return ""
577        }
578        validate {
579            set path [lindex $args 0]
580            set value [lindex $args 1]
581            set units [$_xmlobj get $path.units]
582
583            if {"" != $units} {
584                set nv [Rappture::Units::convert \
585                    $value -context $units -to $units -units off]
586            } else {
587                set nv $value
588            }
589            if {![string is double $nv]
590                  || [regexp -nocase {^(inf|nan)$} $nv]} {
591                error "Value out of range"
592            }
593
594            set rawmin [$_xmlobj get $path.min]
595            if {"" != $rawmin} {
596                set minv $rawmin
597                if {"" != $units} {
598                    set minv [Rappture::Units::convert \
599                        $minv -context $units -to $units -units off]
600                    set nv [Rappture::Units::convert \
601                        $value -context $units -to $units -units off]
602                }
603                # fix for the case when the user tries to
604                # compare values like minv=-500 nv=-0600
605                set nv [format "%g" $nv]
606                set minv [format "%g" $minv]
607
608                if {$nv < $minv} {
609                    error "Minimum value allowed here is $rawmin"
610                }
611            }
612
613            set rawmax [$_xmlobj get $path.max]
614            if {"" != $rawmax} {
615                set maxv $rawmax
616                if {"" != $units} {
617                    set maxv [Rappture::Units::convert \
618                        $maxv -context $units -to $units -units off]
619                    set nv [Rappture::Units::convert \
620                        $value -context $units -to $units -units off]
621                }
622                # fix for the case when the user tries to
623                # compare values like maxv=-500 nv=-0600
624                set nv [format "%g" $nv]
625                set maxv [format "%g" $maxv]
626
627                if {$nv > $maxv} {
628                    error "Maximum value allowed here is $rawmax"
629                }
630            }
631
632            return "ok"
633        }
634        put {
635            set path [lindex $args 0]
636            set value [lindex $args 1]
637            $_xmlobj put $path.current $value
638            Build
639        }
640        default {
641            error "bad field controls option \"$option\": should be get or put"
642        }
643    }
644}
645
646# ----------------------------------------------------------------------
647# USAGE: hints ?<keyword>?
648#
649# Returns a list of key/value pairs for various hints about plotting
650# this field.  If a particular <keyword> is specified, then it returns
651# the hint for that <keyword>, if it exists.
652# ----------------------------------------------------------------------
653itcl::body Rappture::Field::hints {{keyword ""}} {
654    if { $keyword != "" } {
655        if {[info exists _hints($keyword)]} {
656            return $_hints($keyword)
657        }
658        return ""
659    }
660    return [array get _hints]
661}
662
663
664# ----------------------------------------------------------------------
665# USAGE: InitHints
666#
667# Returns a list of key/value pairs for various hints about plotting
668# this field.  If a particular <keyword> is specified, then it returns
669# the hint for that <keyword>, if it exists.
670# ----------------------------------------------------------------------
671itcl::body Rappture::Field::InitHints {} {
672    foreach {key path} {
673        camera          camera.position
674        color           about.color
675        default         about.default
676        group           about.group
677        label           about.label
678        scale           about.scale
679        seeds           about.seeds
680        style           about.style
681        type            about.type
682        xlabel          about.xaxis.label
683        ylabel          about.yaxis.label
684        zlabel          about.zaxis.label
685        xunits          about.xaxis.units
686        yunits          about.yaxis.units
687        zunits          about.zaxis.units
688        units           units
689        updir           updir
690        vectors         about.vectors
691    } {
692        set str [$_field get $path]
693        if { "" != $str } {
694            set _hints($key) $str
695        }
696    }
697    foreach cname [components] {
698        if { ![info exists _comp2mesh($cname)] } {
699            continue
700        }
701        set mesh [lindex $_comp2mesh($cname) 0]
702        foreach axis {x y z} {
703            if { ![info exists _hints(${axis}units)] } {
704                set _hints(${axis}units) [$mesh units $axis]
705            }
706            if { ![info exists _hints(${axis}label)] } {
707                set _hints(${axis}label) [$mesh label $axis]
708            }
709        }
710    }
711    foreach {key path} {
712        toolid          tool.id
713        toolname        tool.name
714        toolcommand     tool.execute
715        tooltitle       tool.title
716        toolrevision    tool.version.application.revision
717    } {
718        set str [$_xmlobj get $path]
719        if { "" != $str } {
720            set _hints($key) $str
721        }
722    }
723    # Set toolip and path hints
724    set _hints(path) $_path
725    if { [info exists _hints(group)] && [info exists _hints(label)] } {
726        # pop-up help for each curve
727        set _hints(tooltip) $_hints(label)
728    }
729}
730
731# ----------------------------------------------------------------------
732# USAGE: Build
733#
734# Used internally to build up the vector representation for the
735# field when the object is first constructed, or whenever the field
736# data changes.  Discards any existing vectors and builds everything
737# from scratch.
738# ----------------------------------------------------------------------
739itcl::body Rappture::Field::Build {} {
740
741    # Discard any existing data
742    foreach name [array names _comp2xy] {
743        eval blt::vector destroy $_comp2xy($name)
744    }
745    array unset _comp2vtk
746    foreach name [array names _comp2unirect2d] {
747        eval itcl::delete object $_comp2unirect2d($name)
748    }
749    foreach name [array names _comp2unirect3d] {
750        eval itcl::delete object $_comp2unirect3d($name)
751    }
752    catch {unset _comp2xy}
753    catch {unset _comp2dx}
754    catch {unset _comp2dims}
755    catch {unset _comp2style}
756    array unset _comp2unirect2d
757    array unset _comp2unirect3d
758    array unset _comp2extents
759    array unset _dataobj2type
760    #
761    # Scan through the components of the field and create
762    # vectors for each part.
763    #
764    array unset _isValidComponent
765    foreach cname [$_field children -type component] {
766        set type ""
767        if { ([$_field element $cname.constant] != "" &&
768              [$_field element $cname.domain] != "") ||
769              [$_field element $cname.xy] != "" } {
770            set type "1D"
771        } elseif { [$_field element $cname.mesh] != "" &&
772                   [$_field element $cname.values] != ""} {
773            set type "points-on-mesh"
774        } elseif { [$_field element $cname.vtk] != ""} {
775            set type "vtk"
776            set viewer [$_field get "about.view"]
777            if { $viewer != "" } {
778                set _viewer $viewer
779            }
780        } elseif {[$_field element $cname.opendx] != ""} {
781            global env
782            if { [info exists env(VTKVOLUME)] } {
783                set _viewer "vtkvolume"
784            }
785            set type "opendx"
786        } elseif {[$_field element $cname.dx] != ""} {
787            global env
788            if { [info exists env(VTKVOLUME)] } {
789                set _viewer "vtkvolume"
790            }
791            set type "dx"
792        } elseif {[$_field element $cname.ucd] != ""} {
793            set type "ucd"
794        }
795        set _comp2style($cname) ""
796        if { $type == "" } {
797            puts stderr "WARNING: Ignoring field component \"$_path.$cname\": no data found."
798            continue
799        }
800        # Save the extents of the component
801        if { [$_field element $cname.extents] != "" } {
802            set extents [$_field get $cname.extents]
803        } else {
804            set extents 1
805        }
806        set _comp2extents($cname) $extents
807        set _type $type
808
809        GetTypeAndSize $cname
810        GetAssociation $cname
811        if { $_comp2size($cname) > 1 } {
812            set viewer [$_field get "about.view"]
813            if { $viewer == "" } {
814                set _viewer "glyphs"
815            }
816        }
817        if {$type == "1D"} {
818            #
819            # 1D data can be represented as 2 BLT vectors,
820            # one for x and the other for y.
821            #
822            set xv ""
823            set yv ""
824            set _dim 1
825            set val [$_field get $cname.constant]
826            if {$val != ""} {
827                set domain [$_field get $cname.domain]
828                if {$domain == "" || ![info exists _limits($domain)]} {
829                    set z0 0
830                    set z1 $_zmax
831                } else {
832                    foreach {z0 z1} $_limits($domain) { break }
833                }
834                set xv [blt::vector create x$_counter]
835                $xv append $z0 $z1
836
837                foreach {val pcomp} [_getValue $val] break
838                set yv [blt::vector create y$_counter]
839                $yv append $val $val
840
841                if {$pcomp != ""} {
842                    set zm [expr {0.5*($z0+$z1)}]
843                    set _comp2cntls($cname) \
844                        [list $pcomp $zm $val "$val$_units"]
845                }
846            } else {
847                set xydata [$_field get $cname.xy]
848                if {"" != $xydata} {
849                    set xv [blt::vector create x$_counter]
850                    set yv [blt::vector create y$_counter]
851                    set tmp [blt::vector create \#auto]
852                    $tmp set $xydata
853                    $tmp split $xv $yv
854                    blt::vector destroy $tmp
855                }
856            }
857
858            if {$xv != "" && $yv != ""} {
859                # sort x-coords in increasing order
860                $xv sort $yv
861                set _dim 1
862                set _comp2dims($cname) "1D"
863                set _comp2xy($cname) [list $xv $yv]
864                incr _counter
865            }
866        } elseif {$type == "points-on-mesh"} {
867            if { ![BuildPointsOnMesh $cname] } {
868                continue;               # Ignore this component
869            }
870        } elseif {$type == "vtk"} {
871            set contents [$_field get $cname.vtk]
872            if { $contents == "" } {
873                puts stderr "WARNING: No data for \"$_path.$cname.vtk\""
874                continue;               # Ignore this component
875            }
876            ReadVtkDataSet $cname $contents
877            set _comp2vtk($cname) $contents
878            set _comp2style($cname) [$_field get $cname.style]
879            incr _counter
880        } elseif {$type == "dx" || $type == "opendx" } {
881            #
882            # HACK ALERT!  Extract gzipped, base64-encoded OpenDX
883            # data.  Assume that it's 3D.  Pass it straight
884            # off to the NanoVis visualizer.
885            #
886            set viewer [$_field get "about.view"]
887            if { $viewer != "" } {
888                set _viewer $viewer
889            }
890            if { $_viewer == "" } {
891                if {[$_field element $cname.flow] != ""} {
892                    set _viewer "flowvis"
893                } else {
894                    set _viewer "nanovis"
895                }
896            }
897            set _dim 3
898            set _comp2dims($cname) "3D"
899            if { $_viewer != "nanovis" } {
900                set vtkdata  [$_field get -decode yes $cname.$type]
901                if { $vtkdata == "" } {
902                    puts stderr "WARNING: No data for \"$_path.$cname.$type\""
903                    continue;               # Ignore this component
904                }
905                if 0 {
906                    set f [open /tmp/$_path.$cname.dx "w"]
907                    puts -nonewline $f $vtkdata
908                    close $f
909                }
910                set vtkdata  [Rappture::DxToVtk $vtkdata]
911                if 0 {
912                    set f [open /tmp/$_path.$cname.vtk "w"]
913                    puts -nonewline $f $vtkdata
914                    close $f
915                }
916                ReadVtkDataSet $cname $vtkdata
917                set _type "vtk"
918                set _comp2vtk($cname) $vtkdata
919            } else {
920                set contents [$_field get -decode no $cname.$type]
921                if { $contents == "" } {
922                    puts stderr "WARNING: No data for \"$_path.$cname.$type\""
923                    continue;               # Ignore this component
924                }
925                set _type "dx"
926                set _comp2dx($cname) $contents
927            }
928            set _comp2style($cname) [$_field get $cname.style]
929            if {[$_field element $cname.flow] != ""} {
930                set _comp2flowhints($cname) \
931                    [Rappture::FlowHints ::\#auto $_field $cname $_units]
932            }
933            incr _counter
934        } elseif { $type == "ucd"} {
935            set contents [$_field get $cname.ucd]
936            if { $contents == "" } {
937                continue;               # Ignore this component
938            }
939            set vtkdata [AvsToVtk $cname $contents]
940            ReadVtkDataSet $cname $vtkdata
941            set _comp2vtk($cname) $vtkdata
942            set _comp2style($cname) [$_field get $cname.style]
943            incr _counter
944        }
945        set _isValidComponent($cname) 1
946    }
947    if { [array size _isValidComponent] == 0 } {
948        puts stderr "ERROR: All components of field \"$_path\" are invalid."
949        return 0
950    }
951    # Sanity check.  Verify that all components of the field have the same
952    # dimension.
953    set dim ""
954    foreach cname [array names _comp2dims] {
955        if { $dim == "" } {
956            set dim $_comp2dims($cname)
957            continue
958        }
959        if { $dim != $_comp2dims($cname) } {
960            puts stderr "WARNING: A field can't have components of different dimensions: [join [array get _comp2dims] ,]"
961            return 0
962        }
963    }
964
965    # FIXME: about.scalars and about.vectors are temporary.  With views
966    #        the label and units for each field will be specified there.
967    #
968    # FIXME: Test that every <field><component> has the same field names,
969    #        units, components.
970    #
971    # Override what we found in the VTK file with names that the user
972    # selected.  We override the field label and units.
973    foreach { fname label units } [$_field get about.scalars] {
974        if { ![info exists _fld2Name($fname)] } {
975            set _fld2Name($fname) $fname
976            set _fld2Components($fname) 1
977        }
978        set _fld2Label($fname) $label
979        set _fld2Units($fname) $units
980    }
981    foreach { fname label units } [$_field get about.vectors] {
982        if { ![info exists _fld2Name($fname)] } {
983            set _fld2Name($fname) $fname
984            # We're just marking the field as vector (> 1) for now.
985            set _fld2Components($fname) 3
986        }
987        set _fld2Label($fname) $label
988        set _fld2Units($fname) $units
989    }
990    set _isValid 1
991    return 1
992}
993
994# ----------------------------------------------------------------------
995# USAGE: _getValue <expr>
996#
997# Used internally to get the value for an expression <expr>.  Returns
998# a list of the form {val parameterPath}, where val is the numeric
999# value of the expression, and parameterPath is the XML path to the
1000# parameter representing the value, or "" if the <expr> does not
1001# depend on any parameters.
1002# ----------------------------------------------------------------------
1003itcl::body Rappture::Field::_getValue {expr} {
1004    #
1005    # First, look for the expression among the <parameter>'s
1006    # associated with the device.
1007    #
1008    set found 0
1009    foreach pcomp [$_xmlobj children parameters] {
1010        set id [$_xmlobj element -as id parameters.$pcomp]
1011        if {[string equal $id $expr]} {
1012            set val [$_xmlobj get parameters.$pcomp.current]
1013            if {"" == $val} {
1014                set val [$_xmlobj get parameters.$pcomp.default]
1015            }
1016            if {"" != $val} {
1017                set expr $val
1018                set found 1
1019                break
1020            }
1021        }
1022    }
1023    if {$found} {
1024        set pcomp "parameters.$pcomp"
1025    } else {
1026        set pcomp ""
1027    }
1028
1029    if {$_units != ""} {
1030        set expr [Rappture::Units::convert $expr \
1031            -context $_units -to $_units -units off]
1032    }
1033
1034    return [list $expr $pcomp]
1035}
1036
1037#
1038# isunirect2d  --
1039#
1040# Returns if the field is a unirect2d object. 
1041#
1042itcl::body Rappture::Field::isunirect2d { } {
1043    return [expr [array size _comp2unirect2d] > 0]
1044}
1045
1046#
1047# isunirect3d  --
1048#
1049# Returns if the field is a unirect3d object. 
1050#
1051itcl::body Rappture::Field::isunirect3d { } {
1052    return [expr [array size _comp2unirect3d] > 0]
1053}
1054
1055#
1056# flowhints  --
1057#
1058# Returns the hints associated with a flow vector field. 
1059#
1060itcl::body Rappture::Field::flowhints { cname } {
1061    if { [info exists _comp2flowhints($cname)] } {
1062        return $_comp2flowhints($cname)
1063    }
1064    return ""
1065}
1066
1067#
1068# style  --
1069#
1070# Returns the style associated with a component of the field. 
1071#
1072itcl::body Rappture::Field::style { cname } {
1073    if { [info exists _comp2style($cname)] } {
1074        return $_comp2style($cname)
1075    }
1076    return ""
1077}
1078
1079#
1080# type  --
1081#
1082# Returns the data storage type of the field.
1083#
1084# FIXME: What are the valid types?
1085#
1086itcl::body Rappture::Field::type {} {
1087    return $_type
1088}
1089
1090#
1091# numComponents --
1092#
1093# Returns the number of components in the field component.
1094#
1095itcl::body Rappture::Field::numComponents {cname} {
1096    set name $cname
1097    regsub -all { } $name {_} name
1098    if {[info exists _fld2Components($name)] } {
1099        return $_fld2Components($name)
1100    }
1101    return 1;                           # Default to scalar.
1102}
1103
1104#
1105# extents --
1106#
1107# Returns if the field is a unirect2d object. 
1108#
1109itcl::body Rappture::Field::extents {{cname -overall}} {
1110    if {$cname == "-overall" } {
1111        set max 0
1112        foreach cname [$_field children -type component] {
1113            if { ![info exists _comp2unirect3d($cname)] &&
1114                 ![info exists _comp2extents($cname)] } {
1115                continue
1116            }
1117            set value $_comp2extents($cname)
1118            if { $max < $value } {
1119                set max $value
1120            }
1121        }
1122        return $max
1123    }
1124    if { $cname == "component0"} {
1125        set cname [lindex [components -name] 0]
1126    }
1127    return $_comp2extents($cname)
1128}
1129
1130itcl::body Rappture::Field::VerifyVtkDataSet { contents } {
1131    package require vtk
1132
1133    set reader $this-datasetreader
1134    vtkDataSetReader $reader
1135
1136    # Write the contents to a file just in case it's binary.
1137    set tmpfile file[pid].vtk
1138    set f [open "$tmpfile" "w"]
1139    fconfigure $f -translation binary -encoding binary
1140    puts $f $contents
1141    close $f
1142
1143    $reader SetFileName $tmpfile
1144    $reader ReadAllNormalsOn
1145    $reader ReadAllTCoordsOn
1146    $reader ReadAllScalarsOn
1147    $reader ReadAllColorScalarsOn
1148    $reader ReadAllVectorsOn
1149    $reader ReadAllTensorsOn
1150    $reader ReadAllFieldsOn
1151    $reader Update
1152    file delete $tmpfile
1153
1154    set dataset [$reader GetOutput]
1155    set dataAttrs [$dataset GetPointData]
1156    if { $dataAttrs == ""} {
1157        puts stderr "WARNING: No point data found in \"$_path\""
1158        rename $reader ""
1159        return 0
1160    }
1161    rename $reader ""
1162}
1163
1164itcl::body Rappture::Field::ReadVtkDataSet { cname contents } {
1165    package require vtk
1166
1167    set reader $this-datasetreader
1168    vtkDataSetReader $reader
1169
1170    # Write the contents to a file just in case it's binary.
1171    set tmpfile file[pid].vtk
1172    set f [open "$tmpfile" "w"]
1173    fconfigure $f -translation binary -encoding binary
1174    puts $f $contents
1175    close $f
1176
1177    $reader SetFileName $tmpfile
1178    $reader ReadAllNormalsOn
1179    $reader ReadAllTCoordsOn
1180    $reader ReadAllScalarsOn
1181    $reader ReadAllColorScalarsOn
1182    $reader ReadAllVectorsOn
1183    $reader ReadAllTensorsOn
1184    $reader ReadAllFieldsOn
1185    $reader Update
1186    file delete $tmpfile
1187
1188    set dataset [$reader GetOutput]
1189    set limits {}
1190    foreach {xmin xmax ymin ymax zmin zmax} [$dataset GetBounds] break
1191    # Figure out the dimension of the mesh from the bounds.
1192    set _dim 0
1193    if { $xmax > $xmin } {
1194        incr _dim
1195    }
1196    if { $ymax > $ymin } {
1197        incr _dim
1198    }
1199    if { $zmax > $zmin } {
1200        incr _dim
1201    }
1202    if { $_viewer == "" } {
1203        if { $_dim == 2 } {
1204            set _viewer contour
1205        } else {
1206            set _viewer isosurface
1207        }
1208    }
1209    set _comp2dims($cname) ${_dim}D
1210    if { $_dim < 2 } {
1211        set numPoints [$dataset GetNumberOfPoints]
1212        set xv [blt::vector create \#auto]
1213        for { set i 0 } { $i < $numPoints } { incr i } {
1214            set point [$dataset GetPoint $i]
1215            $xv append [lindex $point 0]
1216        }
1217        set yv [blt::vector create \#auto]
1218        set dataAttrs [$dataset GetPointData]
1219        if { $dataAttrs == ""} {
1220            puts stderr "WARNING: No point data found in \"$_path\""
1221            rename $reader ""
1222            return 0
1223        }
1224        set array [$dataAttrs GetScalars]
1225        if { $array == ""} {
1226            puts stderr "WARNING: No scalar point data found in \"$_path\""
1227            rename $reader ""
1228            return 0
1229        }
1230        set numTuples [$array GetNumberOfTuples]
1231        for { set i 0 } { $i < $numTuples } { incr i } {
1232            $yv append [$array GetComponent $i 0]
1233        }
1234        $xv sort $yv
1235        set _comp2xy($cname) [list $xv $yv]
1236    }
1237    lappend limits x [list $xmin $xmax]
1238    lappend limits y [list $ymin $ymax]
1239    lappend limits z [list $zmin $zmax]
1240    set dataAttrs [$dataset GetPointData]
1241    if { $dataAttrs == ""} {
1242        puts stderr "WARNING: No point data found in \"$_path\""
1243        rename $reader ""
1244        return 0
1245    }
1246    set vmin 0
1247    set vmax 1
1248    set numArrays [$dataAttrs GetNumberOfArrays]
1249    if { $numArrays > 0 } {
1250        set array [$dataAttrs GetArray 0]
1251        # Calling GetRange with component set to -1 will return
1252        # either the scalar range or vector magnitude range
1253        foreach {vmin vmax} [$array GetRange -1] break
1254
1255        for {set i 0} {$i < [$dataAttrs GetNumberOfArrays] } {incr i} {
1256            set array [$dataAttrs GetArray $i]
1257            set fname  [$dataAttrs GetArrayName $i]
1258            foreach {min max} [$array GetRange -1] break
1259            lappend limits $fname [list $min $max]
1260            set _fld2Units($fname) ""
1261            set _fld2Label($fname) $fname
1262            # Let the VTK file override the <type> designated.
1263            set _fld2Components($fname) [$array GetNumberOfComponents]
1264            lappend _comp2fldName($cname) $fname
1265        }
1266    }
1267   
1268    lappend limits v [list $vmin $vmax]
1269    set _comp2limits($cname) $limits
1270
1271    rename $reader ""
1272}
1273
1274#
1275# vtkdata --
1276#
1277#       Returns a string representing the mesh and field data for a specific
1278#       component in the legacy VTK file format.
1279#
1280itcl::body Rappture::Field::vtkdata {cname} {
1281    if {$cname == "component0"} {
1282        set cname "component"
1283    }
1284    # DX: Convert DX to VTK
1285    if {[info exists _comp2dx($cname)]} {
1286        set data $_comp2dx($cname)
1287        set data [Rappture::encoding::decode $data]
1288        return [Rappture::DxToVtk $data]
1289    }
1290    # Unirect3d: isosurface
1291    if {[info exists _comp2unirect3d($cname)]} {
1292        return [$_comp2unirect3d($cname) vtkdata]
1293    }
1294    # VTK file data:
1295    if { [info exists _comp2vtk($cname)] } {
1296        return $_comp2vtk($cname)
1297    }
1298    # Points on mesh:  Construct VTK file output.
1299    if { [info exists _comp2mesh($cname)] } {
1300        # Data is in the form mesh and vector
1301        foreach {mesh vector} $_comp2mesh($cname) break
1302        set label $cname
1303        regsub -all { } $label {_} label
1304        append out "# vtk DataFile Version 3.0\n"
1305        append out "[hints label]\n"
1306        append out "ASCII\n"
1307        append out [$mesh vtkdata]
1308
1309        if { $_comp2assoc($cname) == "pointdata" } {
1310            set vtkassoc "POINT_DATA"
1311        } elseif { $_comp2assoc($cname) == "celldata" } {
1312            set vtkassoc "CELL_DATA"
1313        } elseif { $_comp2assoc($cname) == "fielddata" } {
1314            set vtkassoc "FIELD"
1315        } else {
1316            error "unknown association \"$_comp2assoc($cname)\""
1317        }
1318        set elemSize [numComponents $cname]
1319        set numValues [expr [$vector length] / $elemSize]
1320        if { $_comp2assoc($cname) == "fielddata" } {
1321            append out "$vtkassoc FieldData 1\n"
1322            append out "$label $elemSize $numValues double\n"
1323        } else {
1324            append out "$vtkassoc $numValues\n"
1325            if { $_comp2type($cname) == "colorscalars" } {
1326                # Must be float for ASCII, unsigned char for BINARY
1327                append out "COLOR_SCALARS $label $elemSize\n"
1328            } elseif { $_comp2type($cname) == "normals" } {
1329                # elemSize must equal 3
1330                append out "NORMALS $label double\n"
1331            } elseif { $_comp2type($cname) == "scalars" } {
1332                # elemSize can be 1, 2, 3 or 4
1333                append out "SCALARS $label double $elemSize\n"
1334                append out "LOOKUP_TABLE default\n"
1335            } elseif { $_comp2type($cname) == "tcoords" } {
1336                # elemSize must be 1, 2, or 3
1337                append out "TEXTURE_COORDINATES $label $elemSize double\n"
1338            } elseif { $_comp2type($cname) == "tensors" } {
1339                # elemSize must equal 9
1340                append out "TENSORS $label double\n"
1341            } elseif { $_comp2type($cname) == "vectors" } {
1342                # elemSize must equal 3
1343                append out "VECTORS $label double\n"
1344            } else {
1345                error "unknown element type \"$_comp2type($cname)\""
1346            }
1347        }
1348        append out [$vector range 0 end]
1349        append out "\n"
1350        if 0 {
1351            VerifyVtkDataSet $out
1352        }
1353        return $out
1354    }
1355    error "can't find vtkdata for $cname. This method should only be called by the vtkheightmap widget"
1356}
1357
1358#
1359# BuildPointsOnMesh --
1360#
1361#       Parses the field XML description to build a mesh and values vector
1362#       representing the field.  Right now we handle the deprecated types
1363#       of "cloud", "unirect2d", and "unirect3d" (mostly for flows).
1364#
1365itcl::body Rappture::Field::BuildPointsOnMesh {cname} {
1366    #
1367    # More complex 2D/3D data is represented by a mesh
1368    # object and an associated vector for field values.
1369    #
1370    set path [$_field get $cname.mesh]
1371    if {[$_xmlobj element $path] == ""} {
1372        # Unknown mesh designated.
1373        return 0
1374    }
1375    set viewer [$_field get "about.view"]
1376    if { $viewer != "" } {
1377        set _viewer $viewer
1378    }
1379    set element [$_xmlobj element -as type $path]
1380    set name $cname
1381    regsub -all { } $name {_} name
1382    set _fld2Label($name) $name
1383    set label [hints zlabel]
1384    if { $label != "" } {
1385        set _fld2Label($name) $label
1386    }
1387    set _fld2Units($name) [hints zunits]
1388    set _fld2Components($name) $_comp2size($cname)
1389    lappend _comp2fldName($cname) $name
1390
1391    # Handle bizarre cases that hopefully will be deprecated.
1392    if { $element == "unirect3d" } {
1393        # Special case: unirect3d (should be deprecated) + flow.
1394        if { [$_field element $cname.extents] != "" } {
1395            set vectorsize [$_field get $cname.extents]
1396        } else {
1397            set vectorsize 1
1398        }
1399        set _dim 3
1400        if { $_viewer == "" } {
1401            set _viewer flowvis
1402        }
1403        set _comp2dims($cname) "3D"
1404        set _comp2unirect3d($cname) \
1405            [Rappture::Unirect3d \#auto $_xmlobj $_field $cname $vectorsize]
1406        set _comp2style($cname) [$_field get $cname.style]
1407        if {[$_field element $cname.flow] != ""} {
1408            set _comp2flowhints($cname) \
1409                [Rappture::FlowHints ::\#auto $_field $cname $_units]
1410        }
1411        incr _counter
1412        return 1
1413    }
1414    if { $element == "unirect2d" && [$_field element $cname.flow] != "" } {
1415        # Special case: unirect2d (normally deprecated) + flow.
1416        if { [$_field element $cname.extents] != "" } {
1417            set vectorsize [$_field get $cname.extents]
1418        } else {
1419            set vectorsize 1
1420        }
1421        set _dim 2
1422        if { $_viewer == "" } {
1423            set _viewer "flowvis"
1424        }
1425        set _comp2dims($cname) "2D"
1426        set _comp2unirect2d($cname) \
1427            [Rappture::Unirect2d \#auto $_xmlobj $path]
1428        set _comp2style($cname) [$_field get $cname.style]
1429        set _comp2flowhints($cname) \
1430            [Rappture::FlowHints ::\#auto $_field $cname $_units]
1431        set _values [$_field get $cname.values]
1432        set limits {}
1433        foreach axis { x y z } {
1434            lappend limits $axis [$_comp2unirect2d($cname) limits $axis]
1435        }
1436        set xv [blt::vector create \#auto]
1437        $xv set $_values
1438        lappend limits $cname [$xv limits]
1439        lappend limits v [$xv limits]
1440        blt::vector destroy $xv
1441        set _comp2limits($cname) $limits
1442        incr _counter
1443        return 1
1444    }
1445    switch -- $element {
1446        "cloud" {
1447            set mesh [Rappture::Cloud::fetch $_xmlobj $path]
1448        }
1449        "mesh" {
1450            set mesh [Rappture::Mesh::fetch $_xmlobj $path]
1451        }           
1452        "unirect2d" {
1453            if { $_viewer == "" } {
1454                set _viewer "heightmap"
1455            }
1456            set mesh [Rappture::Unirect2d::fetch $_xmlobj $path]
1457        }
1458    }
1459    if { ![$mesh isvalid] } {
1460        puts stderr "Mesh is invalid"
1461        return 0
1462    }
1463    set _dim [$mesh dimensions]
1464    if { $_dim == 3 } {
1465        set dim 0
1466        foreach axis {x y z} {
1467            foreach {min max} [$mesh limits $axis] {
1468                if { $min < $max } {
1469                    incr dim
1470                }
1471            }
1472        }
1473        if { $dim != 3 } {
1474            set _dim $dim
1475        }
1476    }
1477
1478    if {$_dim == 1} {
1479        # 1D data: Create vectors for graph widget.
1480        # Is this used anywhere?
1481        #
1482        # OOPS!  This is 1D data
1483        # Forget the cloud/field -- store BLT vectors
1484        #
1485        # Is there a natural growth path in generating output from 1D to
1486        # higher dimensions?  If there isn't, let's kill this in favor
1487        # or explicitly using a <curve> instead.  Otherwise, the features
1488        # (methods such as xmarkers) or the <curve> need to be added
1489        # to the <field>.
1490        #
1491        set xv [blt::vector create x$_counter]
1492        set yv [blt::vector create y$_counter]
1493       
1494        $yv set [$mesh points]
1495        $xv seq 0 1 [$yv length]
1496        # sort x-coords in increasing order
1497        $xv sort $yv
1498       
1499        set _comp2dims($cname) "1D"
1500        set _comp2xy($cname) [list $xv $yv]
1501        incr _counter
1502        return 1
1503    }
1504    if {$_dim == 2} {
1505        # 2D data: By default surface or contour plot using heightmap widget.
1506        set _type "heightmap"
1507        set v [blt::vector create \#auto]
1508        $v set [$_field get $cname.values]
1509        if { [$v length] == 0 } {
1510            return 0
1511        }
1512        if { $_viewer == "" } {
1513            set _viewer "contour"
1514        }
1515        set numFieldValues [$v length]
1516        set numComponentsPerTuple [numComponents $cname]
1517        if { [expr $numFieldValues % $numComponentsPerTuple] != 0 } {
1518            puts stderr "ERROR: Number of field values ($numFieldValues) not divisble by elemsize ($numComponentsPerTuple)"
1519            return 0
1520        }
1521        set numFieldTuples [expr $numFieldValues / $numComponentsPerTuple]
1522        if { $_comp2assoc($cname) == "pointdata" } {
1523            set numPoints [$mesh numpoints]
1524            if { $numPoints != $numFieldTuples } {
1525                puts stderr "ERROR: Number of points in mesh ($numPoints) and number of field tuples ($numFieldTuples) don't agree"
1526                return 0
1527            }
1528        } elseif { $_comp2assoc($cname) == "celldata" } {
1529            set numCells [$mesh numcells]
1530            if { $numCells != $numFieldTuples } {
1531                puts stderr "ERROR: Number of cells in mesh ($numCells) and number of field tuples ($numFieldTuples) don't agree"
1532                return 0
1533            }
1534        }
1535        set _comp2dims($cname) "[$mesh dimensions]D"
1536        set _comp2mesh($cname) [list $mesh $v]
1537        set _comp2style($cname) [$_field get $cname.style]
1538        incr _counter
1539        array unset _comp2limits $cname
1540        foreach axis { x y z } {
1541            lappend _comp2limits($cname) $axis [$mesh limits $axis]
1542        }
1543        lappend _comp2limits($cname) $cname [$v limits]
1544        lappend _comp2limits($cname) v [$v limits]
1545        return 1
1546    }
1547    if {$_dim == 3} {
1548        # 3D data: By default isosurfaces plot using isosurface widget.
1549        if { $_viewer == "" } {
1550            set _viewer "isosurface"
1551        }
1552        set _type "isosurface"
1553        set v [blt::vector create \#auto]
1554        $v set [$_field get $cname.values]
1555        if { [$v length] == 0 } {
1556            return 0
1557        }
1558        set numFieldValues [$v length]
1559        set numComponentsPerTuple [numComponents $cname]
1560        if { [expr $numFieldValues % $numComponentsPerTuple] != 0 } {
1561            puts stderr "ERROR: Number of field values ($numFieldValues) not divisble by elemsize ($numComponentsPerTuple)"
1562            return 0
1563        }
1564        set numFieldTuples [expr $numFieldValues / $numComponentsPerTuple]
1565        if { $_comp2assoc($cname) == "pointdata" } {
1566            set numPoints [$mesh numpoints]
1567            if { $numPoints != $numFieldTuples } {
1568                puts stderr "ERROR: Number of points in mesh ($numPoints) and number of field tuples ($numFieldTuples) don't agree"
1569                return 0
1570            }
1571        } elseif { $_comp2assoc($cname) == "celldata" } {
1572            set numCells [$mesh numcells]
1573            if { $numCells != $numFieldTuples } {
1574                puts stderr "ERROR: Number of cells in mesh ($numCells) and number of field tuples ($numFieldTuples) don't agree"
1575                return 0
1576            }
1577        }
1578        set _comp2dims($cname) "[$mesh dimensions]D"
1579        set _comp2mesh($cname) [list $mesh $v]
1580        set _comp2style($cname) [$_field get $cname.style]
1581        incr _counter
1582        foreach axis { x y z } {
1583            lappend _comp2limits($cname) $axis [$mesh limits $axis]
1584        }
1585        lappend _comp2limits($cname) $cname [$v limits]
1586        lappend _comp2limits($cname) v [$v limits]
1587        return 1
1588    }
1589    error "unhandled case in field dim=$_dim element=$element"
1590}
1591
1592itcl::body Rappture::Field::AvsToVtk { cname contents } {
1593    package require vtk
1594
1595    set reader $this-datasetreader
1596    vtkAVSucdReader $reader
1597
1598    # Write the contents to a file just in case it's binary.
1599    set tmpfile $cname[pid].ucd
1600    set f [open "$tmpfile" "w"]
1601    fconfigure $f -translation binary -encoding binary
1602    puts $f $contents
1603    close $f
1604    $reader SetFileName $tmpfile
1605    $reader Update
1606    file delete $tmpfile
1607
1608    set tmpfile $cname[pid].vtk
1609    set writer $this-datasetwriter
1610    vtkDataSetWriter $writer
1611    $writer SetInputConnection [$reader GetOutputPort]
1612    $writer SetFileName $tmpfile
1613    $writer Write
1614    rename $reader ""
1615    rename $writer ""
1616
1617    set f [open "$tmpfile" "r"]
1618    fconfigure $f -translation binary -encoding binary
1619    set vtkdata [read $f]
1620    close $f
1621    file delete $tmpfile
1622    return $vtkdata
1623}
1624
1625itcl::body Rappture::Field::GetTypeAndSize { cname } {
1626    array set type2components {
1627        "colorscalars"         4
1628        "normals"              3
1629        "scalars"              1
1630        "tcoords"              2
1631        "tensors"              9
1632        "vectors"              3
1633    }
1634    set type [$_field get $cname.elemtype]
1635    if { $type == "" } {
1636        set type "scalars"
1637    }
1638    if { ![info exists type2components($type)] } {
1639        error "unknown <elemtype> \"$type\" in field"
1640    }
1641    set size [$_field get $cname.elemsize]
1642    if { $size == "" } {
1643        set size $type2components($type)
1644    }
1645    set _comp2type($cname) $type
1646    set _comp2size($cname) $size
1647}
1648
1649itcl::body Rappture::Field::GetAssociation { cname } {
1650    set assoc [$_field get $cname.association]
1651    if { $assoc == "" } {
1652        set _comp2assoc($cname) "pointdata"
1653        return
1654    }
1655    switch -- $assoc {
1656        "pointdata" - "celldata" - "fielddata" {
1657            set _comp2assoc($cname) $assoc
1658            return
1659        }
1660        default {
1661            error "unknown field association \"$assoc\""
1662        }
1663    }
1664}
Note: See TracBrowser for help on using the repository browser.