source: trunk/gui/scripts/field.tcl @ 3515

Last change on this file since 3515 was 3508, checked in by gah, 11 years ago

fix for no dim tag with old style nodes and elements

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