source: branches/Rappture 1.2/gui/scripts/field.tcl @ 3272

Last change on this file since 3272 was 3272, checked in by gah, 12 years ago

add outline mode, fix limits in triangle mesh

File size: 44.2 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: field - extracts data from an XML description of a field
4#
5#  This object represents one field in an XML description of a device.
6#  It simplifies the process of extracting data vectors that represent
7#  the field.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itcl
16package require BLT
17
18namespace eval Rappture { # forward declaration }
19
20itcl::class Rappture::Field {
21    constructor {xmlobj path} { # defined below }
22    destructor { # defined below }
23
24    public method components {args}
25    public method mesh {{cname -overall}}
26    public method values {{cname -overall}}
27    public method blob {{cname -overall}}
28    public method limits {axis}
29    public method controls {option args}
30    public method hints {{key ""}}
31    public method style { cname }
32    public method isunirect2d {}
33    public method isunirect3d {}
34    public method extents {{cname -overall}}
35    public method flowhints { cname }
36    public method type {}
37    public method viewer {} {
38        return $_viewer
39    }
40    public method vtkdata {cname}
41   
42    protected method _build {}
43    protected method _getValue {expr}
44
45    private variable _dim       0;      # Dimension of mesh
46    private variable _xmlobj ""  ;      # ref to XML obj with device data
47    private variable _path "";          # Path of this object in the XML
48    private variable _units ""   ;      # system of units for this field
49    private variable _limits     ;# maps box name => {z0 z1} limits
50    private variable _zmax 0     ;# length of the device
51
52    private variable _field ""
53    private variable _fieldNames ""   ;# list of field names.
54    private variable _fieldUnits ""   ;# list of units of each field name.
55    private variable _fieldLabels ""  ;# list of labels of each field name.
56    private variable _comp2dims  ;# maps component name => dimensionality
57    private variable _comp2xy    ;# maps component name => x,y vectors
58    private variable _comp2vtk   ;# maps component name => vtkFloatArray
59    private variable _comp2vtkstreamlines   ;# maps component name => vtkFloatArray
60    private variable _comp2vtkcontour   ;# maps component name => vtkFloatArray
61    private variable _comp2vtkvolume   ;# maps component name => vtkFloatArray
62    private variable _comp2volume   ;# maps component name => vtkFloatArray
63    private variable _comp2dx    ;# maps component name => OpenDX data
64    private variable _comp2unirect2d ;# maps component name => unirect2d obj
65    private variable _comp2unirect3d ;# maps component name => unirect3d obj
66    private variable _comp2style ;# maps component name => style settings
67    private variable _comp2cntls ;# maps component name => x,y control points
68    private variable _comp2extents
69    private variable _comp2limits
70    private variable _type ""
71    private variable _comp2flowhints
72    private variable _comp2heightmap
73    private common _counter 0    ;# counter for unique vector names
74
75    private method BuildPointsOnMesh { cname }
76    private method ConvertToVtkData { cname }
77    private method ReadVtkDataSet { cname contents }
78    private variable _isVtk
79    private variable _values ""
80    private variable _viewer "vtkcontour"
81}
82
83# ----------------------------------------------------------------------
84# CONSTRUCTOR
85# ----------------------------------------------------------------------
86itcl::body Rappture::Field::constructor {xmlobj path} {
87    if {![Rappture::library isvalid $xmlobj]} {
88        error "bad value \"$xmlobj\": should be Rappture::library"
89    }
90    set _xmlobj $xmlobj
91    set _path $path
92    set _field [$xmlobj element -as object $path]
93    set _units [$_field get units]
94
95    set xunits [$xmlobj get units]
96    if {"" == $xunits || "arbitrary" == $xunits} {
97        set xunits "um"
98    }
99
100    # determine the overall size of the device
101    set z0 [set z1 0]
102    foreach elem [$_xmlobj children components] {
103        switch -glob -- $elem {
104            box* {
105                if {![regexp {[0-9]$} $elem]} {
106                    set elem "${elem}0"
107                }
108                set z0 [$_xmlobj get components.$elem.corner0]
109                set z0 [Rappture::Units::convert $z0 \
110                    -context $xunits -to $xunits -units off]
111
112                set z1 [$_xmlobj get components.$elem.corner1]
113                set z1 [Rappture::Units::convert $z1 \
114                    -context $xunits -to $xunits -units off]
115
116                set _limits($elem) [list $z0 $z1]
117            }
118        }
119    }
120    set _zmax $z1
121
122    # build up vectors for various components of the field
123    _build
124}
125
126# ----------------------------------------------------------------------
127# DESTRUCTOR
128# ----------------------------------------------------------------------
129itcl::body Rappture::Field::destructor {} {
130    itcl::delete object $_field
131    # don't destroy the _xmlobj! we don't own it!
132
133    foreach name [array names _comp2xy] {
134        eval blt::vector destroy $_comp2xy($name)
135    }
136    foreach name [array names _comp2vtk] {
137        set mobj [lindex $_comp2vtk($name) 0]
138        set class [$mobj info class]
139        ${class}::release $mobj
140
141        set fobj [lindex $_comp2vtk($name) 1]
142        rename $fobj ""
143    }
144    foreach name [array names _comp2unirect2d] {
145        itcl::delete object $_comp2unirect2d($name)
146    }
147    foreach name [array names _comp2unirect3d] {
148        itcl::delete object $_comp2unirect3d($name)
149    }
150    foreach name [array names _comp2flowhints] {
151        itcl::delete object $_comp2flowhints($name)
152    }
153    foreach name [array names _comp2heightmap] {
154        set mesh [lindex $_comp2heightmap($name) 0]
155        set class [$mesh info class]
156        ${class}::release $mesh
157
158        set vtkarray [lindex $_comp2heightmap($name) 1]
159        rename $vtkarray ""
160    }
161}
162
163# ----------------------------------------------------------------------
164# USAGE: components ?-name|-dimensions|-style? ?<pattern>?
165#
166# Returns a list of names or types for the various components of
167# this field.  If the optional glob-style <pattern> is specified,
168# then it returns only the components with names matching the pattern.
169# ----------------------------------------------------------------------
170itcl::body Rappture::Field::components {args} {
171    Rappture::getopts args params {
172        flag what -name default
173        flag what -dimensions
174        flag what -style
175        flag what -particles
176        flag what -flow
177        flag what -box
178    }
179
180    set pattern *
181    if {[llength $args] > 0} {
182        set pattern [lindex $args 0]
183        set args [lrange $args 1 end]
184    }
185    if {[llength $args] > 0} {
186        error "wrong # args: should be \"components ?switches? ?pattern?\""
187    }
188
189    set rlist ""
190
191    # BE CAREFUL: return component names in proper order
192    foreach cname [$_field children -type component] {
193        if {[info exists _comp2dims($cname)]
194              && [string match $pattern $cname]} {
195
196            switch -- $params(what) {
197                -name { lappend rlist $cname }
198                -dimensions { lappend rlist $_comp2dims($cname) }
199                -style { lappend rlist $_comp2style($cname) }
200            }
201        }
202    }
203    return $rlist
204}
205
206# ----------------------------------------------------------------------
207# USAGE: mesh ?<name>?
208#
209# Returns a list {xvec yvec} for the specified field component <name>.
210# If the name is not specified, then it returns the vectors for the
211# overall field (sum of all components).
212# ----------------------------------------------------------------------
213itcl::body Rappture::Field::mesh {{cname -overall}} {
214    if {$cname == "-overall" || $cname == "component0"} {
215        set cname [lindex [components -name] 0]
216    }
217    if {[info exists _comp2xy($cname)]} {
218        return [lindex $_comp2xy($cname) 0]  ;# return xv
219    }
220    if { [info exists _comp2vtkstreamlines($cname)] } {
221        error "mesh: not implemented for streamlines"
222        return [$mobj mesh]
223    }
224    if { [info exists _comp2vtkcontour($cname)] } {
225        error "method \"mesh\" is not implemented for vtkcontour"
226    }
227    if { [info exists _comp2vtk($cname)] } {
228        set mobj [lindex $_comp2vtk($cname) 0]
229        return [$mobj mesh]
230    }
231    if {[info exists _comp2dx($cname)]} {
232        return ""  ;# no mesh -- it's embedded in the value data
233    }
234    if {[info exists _comp2heightmap($cname)]} {
235        return ""  ;# no mesh -- it's embedded in the value data
236    }
237    if {[info exists _comp2vtkvolume($cname)]} {
238        return ""  ;# no mesh -- it's embedded in the value data
239    }
240    if {[info exists _comp2unirect2d($cname)]} {
241        set mobj [lindex $_comp2unirect2d($cname) 0]
242        return [$mobj mesh]
243    }
244    if {[info exists _comp2unirect3d($cname)]} {
245        set mobj [lindex $_comp2unirect3d($cname) 0]
246        return [$mobj mesh]
247    }
248    error "bad option \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
249}
250
251# ----------------------------------------------------------------------
252# USAGE: values ?<name>?
253#
254# Returns a list {xvec yvec} for the specified field component <name>.
255# If the name is not specified, then it returns the vectors for the
256# overall field (sum of all components).
257# ----------------------------------------------------------------------
258itcl::body Rappture::Field::values {{cname -overall}} {
259    if {$cname == "component0"} {
260        set cname "component"
261    }
262    if {[info exists _comp2xy($cname)]} {
263        return [lindex $_comp2xy($cname) 1]  ;# return yv
264    }
265    if { [info exists _comp2vtkcontour($cname)] } {
266        error "method \"values\" is not implemented for vtkcontour"
267    }
268    if { [info exists _comp2heightmap($cname)] } {
269        return $_values
270    }
271    if { [info exists _comp2vtkstreamlines($cname)] } {
272        # FIXME: Need to process the vtk file data to pull out the field's
273        # values.
274        error "vtkstreamlines: values not implemented"
275        return [lindex $_comp2vtkstreamlines($cname) 1]
276    }
277    if { [info exists _comp2vtk($cname)] } {
278        return [lindex $_comp2vtk($cname) 1]  ;# return vtkFloatArray
279    }
280    if {[info exists _comp2dx($cname)]} {
281        return $_comp2dx($cname)  ;# return gzipped, base64-encoded DX data
282    }
283    if {[info exists _comp2unirect2d($cname)]} {
284        return [$_comp2unirect2d($cname) values]
285    }
286    if {[info exists _comp2unirect3d($cname)]} {
287        return [$_comp2unirect3d($cname) blob]
288    }
289    error "bad option \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
290}
291
292# ----------------------------------------------------------------------
293# USAGE: blob ?<name>?
294#
295# Returns a string representing the blob of data for the mesh and values.
296# ----------------------------------------------------------------------
297itcl::body Rappture::Field::blob {{cname -overall}} {
298    if {$cname == "component0"} {
299        set cname "component"
300    }
301    if {[info exists _comp2xy($cname)]} {
302        return ""
303    }
304    if { [info exists _comp2vtk($cname)] } {
305        return ""
306    }
307    if { [info exists _comp2vtkvolume($cname)] } {
308        return $_comp2vtkvolume($cname)
309    }
310    if { [info exists _comp2vtkcontour($cname)] } {
311        return $_comp2vtkcontour($cname)
312    }
313    if { [info exists _comp2vtkstreamlines($cname)] } {
314        # Return the contents of the vtk file.
315        return $_comp2vtkstreamlines($cname)
316    }
317    if {[info exists _comp2dx($cname)]} {
318        return $_comp2dx($cname)  ;# return gzipped, base64-encoded DX data
319    }
320    if {[info exists _comp2unirect2d($cname)]} {
321        return [$_comp2unirect2d($cname) blob]
322    }
323    if {[info exists _comp2unirect3d($cname)]} {
324        return [$_comp2unirect3d($cname) blob]
325    }
326    error "bad option \"$cname\": should be [join [lsort [array names _comp2dims]] {, }]"
327}
328
329# ----------------------------------------------------------------------
330# USAGE: limits <axis>
331#
332# Returns a list {min max} representing the limits for the specified
333# axis.
334# ----------------------------------------------------------------------
335itcl::body Rappture::Field::limits {which} {
336    set min ""
337    set max ""
338    blt::vector tmp zero
339
340    foreach cname [array names _comp2dims] {
341        switch -- $_comp2dims($cname) {
342            1D {
343                switch -- $which {
344                    x - xlin {
345                        set pos 0; set log 0; set axis x
346                    }
347                    xlog {
348                        set pos 0; set log 1; set axis x
349                    }
350                    y - ylin - v - vlin {
351                        set pos 1; set log 0; set axis y
352                    }
353                    ylog - vlog {
354                        set pos 1; set log 1; set axis y
355                    }
356                    default {
357                        error "bad option \"$which\": should be x, xlin, xlog, y, ylin, ylog, v, vlin, vlog"
358                    }
359                }
360
361                set vname [lindex $_comp2xy($cname) $pos]
362                $vname variable vec
363
364                if {$log} {
365                    # on a log scale, use abs value and ignore 0's
366                    $vname dup tmp
367                    $vname dup zero
368                    zero expr {tmp == 0}            ;# find the 0's
369                    tmp expr {abs(tmp)}             ;# get the abs value
370                    tmp expr {tmp + zero*max(tmp)}  ;# replace 0's with abs max
371                    set axisMin [blt::vector expr min(tmp)]
372                    set axisMax [blt::vector expr max(tmp)]
373                } else {
374                    set axisMin $vec(min)
375                    set axisMax $vec(max)
376                }
377
378                if {"" == $min} {
379                    set min $axisMin
380                } elseif {$axisMin < $min} {
381                    set min $axisMin
382                }
383                if {"" == $max} {
384                    set max $axisMax
385                } elseif {$axisMax > $max} {
386                    set max $axisMax
387                }
388            }
389            2D - 3D {
390                if {[info exists _comp2unirect2d($cname)]} {
391                    set limits [$_comp2unirect2d($cname) limits $which]
392                    foreach {axisMin axisMax} $limits break
393                    set axis v
394                } elseif {[info exists _comp2unirect3d($cname)]} {
395                    set limits [$_comp2unirect3d($cname) limits $which]
396                    foreach {axisMin axisMax} $limits break
397                    set axis v
398                } elseif {[info exists _comp2vtk($cname)]} {
399                    foreach {xv yv} $_comp2vtk($cname) break
400                    switch -- $which {
401                        x - xlin - xlog {
402                            foreach {axisMin axisMax} [$xv limits x] break
403                            set axis x
404                        }
405                        y - ylin - ylog {
406                            foreach {axisMin axisMax} [$xv limits y] break
407                            set axis y
408                        }
409                        z - zlin - zlog {
410                            foreach {axisMin axisMax} [$xv limits z] break
411                            set axis z
412                        }
413                        v - vlin - vlog {
414                            catch {unset style}
415                            array set style $_comp2style($cname)
416                            if {[info exists style(-min)] &&
417                                [info exists style(-max)]} {
418                                # This component has its own hard-coded
419                                # min/max range.  Ignore it for overall limits.
420                                set axisMin $min
421                                set axisMax $max
422                            } else {
423                                foreach {axisMin axisMax} [$yv GetRange] break
424                            }
425                            set axis v
426                        }
427                        default {
428                            error "bad option \"$which\": should be x, xlin, xlog, y, ylin, ylog, v, vlin, vlog"
429                        }
430                    }
431                } elseif {[info exists _comp2heightmap($cname)]} {
432                    foreach {mesh vector} $_comp2heightmap($cname) break
433                    switch -- $which {
434                        x - xlin - xlog {
435                            set axis x
436                            foreach {axisMin axisMax} [$mesh limits x] break
437                        }
438                        y - ylin - ylog {
439                            foreach {axisMin axisMax} [$mesh limits y] break
440                            set axis y
441                        }
442                        z - zlin - zlog {
443                            foreach {axisMin axisMax} [$mesh limits z] break
444                            set axis z
445                        }
446                        v - vlin - vlog {
447                            set axisMin [$vector min]
448                            set axisMax [$vector max]
449                            set axis v
450                        }
451                    }
452                } elseif {[info exists _comp2limits($cname)]} {
453                    array set limits $_comp2limits($cname)
454                    switch -- $which {
455                        x - xlin - xlog {
456                            set axis x
457                            set axisMax $limits(xmax)
458                            set axisMin $limits(xmin)
459                        }
460                        y - ylin - ylog {
461                            set axis y
462                            set axisMax $limits(ymax)
463                            set axisMin $limits(ymin)
464                        }
465                        z - zlin - zlog {
466                            set axis y
467                            set axisMax $limits(zmax)
468                            set axisMin $limits(zmin)
469                        }
470                        v - vlin - vlog {
471                            set axis v
472                            set axisMax $limits(vmax)
473                            set axisMin $limits(vmin)
474                        }
475                    }
476                } else {
477                    set axisMin 0  ;# HACK ALERT! must be OpenDX data
478                    set axisMax 1
479                    set axis v
480                }
481            }
482        }
483        if { "" == $min || $axisMin < $min } {
484            set min $axisMin
485        }
486        if { "" == $max || $axisMax > $max } {
487            set max $axisMax
488        }
489    }
490    blt::vector destroy tmp zero
491    set val [$_field get "${axis}axis.min"]
492    if {"" != $val && "" != $min} {
493        if {$val > $min} {
494            # tool specified this min -- don't go any lower
495            set min $val
496        }
497    }
498
499    set val [$_field get "${axis}axis.max"]
500    if {"" != $val && "" != $max} {
501        if {$val < $max} {
502            # tool specified this max -- don't go any higher
503            set max $val
504        }
505    }
506    return [list $min $max]
507}
508
509# ----------------------------------------------------------------------
510# USAGE: controls get ?<name>?
511# USAGE: controls validate <path> <value>
512# USAGE: controls put <path> <value>
513#
514# Returns a list {path1 x1 y1 val1  path2 x2 y2 val2 ...} representing
515# control points for the specified field component <name>.
516# ----------------------------------------------------------------------
517itcl::body Rappture::Field::controls {option args} {
518    switch -- $option {
519        get {
520            set cname [lindex $args 0]
521            if {[info exists _comp2cntls($cname)]} {
522                return $_comp2cntls($cname)
523            }
524            return ""
525        }
526        validate {
527            set path [lindex $args 0]
528            set value [lindex $args 1]
529            set units [$_xmlobj get $path.units]
530
531            if {"" != $units} {
532                set nv [Rappture::Units::convert \
533                    $value -context $units -to $units -units off]
534            } else {
535                set nv $value
536            }
537            if {![string is double $nv]
538                  || [regexp -nocase {^(inf|nan)$} $nv]} {
539                error "Value out of range"
540            }
541
542            set rawmin [$_xmlobj get $path.min]
543            if {"" != $rawmin} {
544                set minv $rawmin
545                if {"" != $units} {
546                    set minv [Rappture::Units::convert \
547                        $minv -context $units -to $units -units off]
548                    set nv [Rappture::Units::convert \
549                        $value -context $units -to $units -units off]
550                }
551                # fix for the case when the user tries to
552                # compare values like minv=-500 nv=-0600
553                set nv [format "%g" $nv]
554                set minv [format "%g" $minv]
555
556                if {$nv < $minv} {
557                    error "Minimum value allowed here is $rawmin"
558                }
559            }
560
561            set rawmax [$_xmlobj get $path.max]
562            if {"" != $rawmax} {
563                set maxv $rawmax
564                if {"" != $units} {
565                    set maxv [Rappture::Units::convert \
566                        $maxv -context $units -to $units -units off]
567                    set nv [Rappture::Units::convert \
568                        $value -context $units -to $units -units off]
569                }
570                # fix for the case when the user tries to
571                # compare values like maxv=-500 nv=-0600
572                set nv [format "%g" $nv]
573                set maxv [format "%g" $maxv]
574
575                if {$nv > $maxv} {
576                    error "Maximum value allowed here is $rawmax"
577                }
578            }
579
580            return "ok"
581        }
582        put {
583            set path [lindex $args 0]
584            set value [lindex $args 1]
585            $_xmlobj put $path.current $value
586            _build
587        }
588        default {
589            error "bad option \"$option\": should be get or put"
590        }
591    }
592}
593
594# ----------------------------------------------------------------------
595# USAGE: hints ?<keyword>?
596#
597# Returns a list of key/value pairs for various hints about plotting
598# this field.  If a particular <keyword> is specified, then it returns
599# the hint for that <keyword>, if it exists.
600# ----------------------------------------------------------------------
601itcl::body Rappture::Field::hints {{keyword ""}} {
602    set hints(fieldnames)  $_fieldNames
603    set hints(fieldunits)  $_fieldUnits
604    set hints(fieldlabels) $_fieldLabels
605    foreach {key path} {
606        camera          camera.position
607        color           about.color
608        default         about.default
609        group           about.group
610        label           about.label
611        fieldnames      about.fieldnames
612        fieldunits      about.fieldunits
613        fieldlabels     about.fieldlabels
614        scale           about.scale
615        seeds           about.seeds
616        style           about.style
617        toolId          tool.id
618        toolName        tool.name
619        toolRevision    tool.version.application.revision
620        type            about.type
621        xlabel          about.xaxis.label
622        ylabel          about.yaxis.label
623        zlabel          about.zaxis.label
624        xunits          about.xaxis.units
625        yunits          about.yaxis.units
626        zunits          about.zaxis.units
627        units           units
628        updir           updir
629        vectors         about.vectors
630    } {
631        set str [$_field get $path]
632        if {"" != $str} {
633            set hints($key) $str
634        }
635    }
636    # Set tool and path hints
637    set hints(tool) [$_xmlobj get tool.name]
638    set hints(path) $_path
639    if 0 {
640        # to be compatible with curve objects
641        set hints(xlabel) "Position"
642    }
643    if {[info exists hints(group)] && [info exists hints(label)]} {
644        # pop-up help for each curve
645        set hints(tooltip) $hints(label)
646    }
647
648    if {$keyword != ""} {
649        if {[info exists hints($keyword)]} {
650            return $hints($keyword)
651        }
652        return ""
653    }
654    return [array get hints]
655}
656
657# ----------------------------------------------------------------------
658# USAGE: _build
659#
660# Used internally to build up the vector representation for the
661# field when the object is first constructed, or whenever the field
662# data changes.  Discards any existing vectors and builds everything
663# from scratch.
664# ----------------------------------------------------------------------
665itcl::body Rappture::Field::_build {} {
666
667    # Discard any existing data
668    foreach name [array names _comp2xy] {
669        eval blt::vector destroy $_comp2xy($name)
670    }
671    foreach name [array names _comp2vtk] {
672        set mobj [lindex $_comp2vtk($name) 0]
673        set class [$mobj info class]
674        ${class}::release $mobj
675
676        set fobj [lindex $_comp2vtk($name) 1]
677        rename $fobj ""
678    }
679    foreach name [array names _comp2unirect2d] {
680        eval itcl::delete object $_comp2unirect2d($name)
681    }
682    foreach name [array names _comp2unirect3d] {
683        eval itcl::delete object $_comp2unirect3d($name)
684    }
685    catch {unset _comp2xy}
686    catch {unset _comp2vtk}
687    catch {unset _comp2dx}
688    catch {unset _comp2dims}
689    catch {unset _comp2style}
690    array unset _comp2volume
691    array unset _comp2vtkstreamlines
692    array unset _comp2vtkcontour
693    array unset _comp2unirect2d
694    array unset _comp2unirect3d
695    array unset _comp2extents
696    array unset _dataobj2type
697    #
698    # Scan through the components of the field and create
699    # vectors for each part.
700    #
701    foreach cname [$_field children -type component] {
702        set type ""
703        if { ([$_field element $cname.constant] != "" &&
704            [$_field element $cname.domain] != "") ||
705            [$_field element $cname.xy] != ""} {
706            set type "1D"
707        } elseif {[$_field element $cname.mesh] != "" &&
708            [$_field element $cname.values] != ""} {
709            set type "points-on-mesh"
710        } elseif {[$_field element $cname.vtk] != ""} {
711            set _isVtkData($cname) 1
712            set view [$_field get "about.view"]
713            switch -- $view {
714                "streamlines" {
715                    set type "vtkstreamlines"
716                }
717                "contour" - "heightmap" {
718                    set type "vtkcontour"
719                }
720                default {
721                    set type "vtk"
722                }
723            }
724        } elseif {[$_field element $cname.opendx] != ""} {
725            global env
726            if { [info exists env(VTKVOLUME)] } {
727                set type "vtkvolume"
728            } else {
729                set type "dx"
730            }
731        } elseif {[$_field element $cname.dx] != ""} {
732            global env
733            if { [info exists env(VTKVOLUME)] } {
734                set type "vtkvolume"
735            } else {
736                set type "dx"
737            }
738        }
739        set _comp2style($cname) ""
740       
741        # Save the extents of the component
742        if { [$_field element $cname.extents] != "" } {
743            set extents [$_field get $cname.extents]
744        } else {
745            set extents 1
746        }
747        set _comp2extents($cname) $extents
748        set _type $type
749        if {$type == "1D"} {
750            #
751            # 1D data can be represented as 2 BLT vectors,
752            # one for x and the other for y.
753            #
754            set xv ""
755            set yv ""
756
757            set val [$_field get $cname.constant]
758            if {$val != ""} {
759                set domain [$_field get $cname.domain]
760                if {$domain == "" || ![info exists _limits($domain)]} {
761                    set z0 0
762                    set z1 $_zmax
763                } else {
764                    foreach {z0 z1} $_limits($domain) { break }
765                }
766                set xv [blt::vector create x$_counter]
767                $xv append $z0 $z1
768
769                foreach {val pcomp} [_getValue $val] break
770                set yv [blt::vector create y$_counter]
771                $yv append $val $val
772
773                if {$pcomp != ""} {
774                    set zm [expr {0.5*($z0+$z1)}]
775                    set _comp2cntls($cname) \
776                        [list $pcomp $zm $val "$val$_units"]
777                }
778            } else {
779                set xydata [$_field get $cname.xy]
780                if {"" != $xydata} {
781                    set xv [blt::vector create x$_counter]
782                    set yv [blt::vector create y$_counter]
783                    set tmp [blt::vector create \#auto]
784                    $tmp set $xydata
785                    $tmp split $xv $yv
786                    blt::vector destroy $tmp
787                }
788            }
789
790            if {$xv != "" && $yv != ""} {
791                # sort x-coords in increasing order
792                $xv sort $yv
793
794                set _comp2dims($cname) "1D"
795                set _comp2xy($cname) [list $xv $yv]
796                incr _counter
797            }
798        } elseif {$type == "points-on-mesh"} {
799            BuildPointsOnMesh $cname
800        } elseif {$type == "vtk"} {
801            #
802            # Extract native vtk data from the XML and use a reader
803            # to load it.
804            #
805            error "not implemented"
806        } elseif {$type == "vtkstreamlines"} {
807            set _comp2dims($cname) "3D"
808            # Allow redirects to another element.
809            set vtkdata [$_field get $cname.vtk]
810            set _comp2vtkstreamlines($cname) $vtkdata
811            set _comp2style($cname) [$_field get $cname.style]
812            incr _counter
813        } elseif {$type == "vtkcontour"} {
814            set _comp2dims($cname) "2D"
815            # Allow redirects to another element.
816
817            set data [$_field get $cname.vtk]
818            ReadVtkDataSet $cname $data
819            set _comp2vtkcontour($cname) $data
820            set _comp2style($cname) [$_field get $cname.style]
821            incr _counter
822        } elseif {$type == "vtkvolume"} {
823            set _comp2dims($cname) "3D"
824            # Allow redirects to another element.
825            set data [$_field get -decode no $cname.dx]
826            set data [Rappture::encoding::decode -as zb64 $data]
827            if 1 {
828            set file "/tmp/$cname.dx"
829            set f [open $file "w"]
830            puts $f $data
831            close $f
832            }
833            set data [Rappture::ConvertDxToVtk $data]
834            if 1 {
835            set file "/tmp/$cname.vtk"
836            set f [open $file "w"]
837            puts $f $data
838            close $f
839            }
840            set _comp2vtkvolume($cname) $data
841            set _comp2style($cname) [$_field get $cname.style]
842            incr _counter
843        } elseif {$type == "vtkstreamlines2"} {
844            set _comp2dims($cname) "3D"
845            set _comp2vtkstreamlines($cname) [$_field get $cname.vtk]
846            set _comp2style($cname) [$_field get $cname.style]
847            incr _counter
848        } elseif {$type == "dx" } {
849            #
850            # HACK ALERT!  Extract gzipped, base64-encoded OpenDX
851            # data.  Assume that it's 3D.  Pass it straight
852            # off to the NanoVis visualizer.
853            #
854            set _comp2dims($cname) "3D"
855            set _comp2dx($cname)  [$_field get -decode no $cname.dx]
856            if 1 {
857            set data  [$_field get -decode yes $cname.dx]
858            set file "/tmp/junk.dx"
859            set f [open $file "w"]
860            puts $f $data
861            close $f
862            if { [string match "<ODX>*" $data] } {
863                set data [string range $data 5 end]
864                set _comp2dx($cname) \
865                        [Rappture::encoding::encode -as zb64 $data]
866            }
867            }
868            set _comp2style($cname) [$_field get $cname.style]
869            if {[$_field element $cname.flow] != ""} {
870                set _comp2flowhints($cname) \
871                    [Rappture::FlowHints ::\#auto $_field $cname $_units]
872            }
873            incr _counter
874        } elseif {$type == "opendx"} {
875            #
876            # HACK ALERT!  Extract gzipped, base64-encoded OpenDX
877            # data.  Assume that it's 3D.  Pass it straight
878            # off to the NanoVis visualizer.
879            #
880            set _comp2dims($cname) "3D"
881            set data [$_field get -decode yes $cname.opendx]
882            set data "<ODX>$data"
883            set data [Rappture::encoding::encode -as zb64 $data]
884            set _comp2dx($cname) $data
885            set _comp2style($cname) [$_field get $cname.style]
886            if {[$_field element $cname.flow] != ""} {
887                set _comp2flowhints($cname) \
888                    [Rapture::FlowHints ::\#auto $_field $cname $_units]
889            }
890            incr _counter
891        }
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 [$_field 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        "vtkcontour" {
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    $reader SetFileName $tmpfile
1119    $reader ReadAllScalarsOn
1120    $reader ReadAllVectorsOn
1121    $reader ReadAllFieldsOn
1122    $reader Update
1123    set dataset [$reader GetOutput]
1124    set limits {}
1125    foreach {xmin xmax ymin ymax zmin zmax} [$dataset GetBounds] break
1126    # Figure out the dimension of the mesh from the bounds.
1127    set _dim 0
1128    if { $xmax > $xmin } {
1129        incr _dim
1130    }
1131    if { $ymax > $ymin } {
1132        incr _dim
1133    }
1134    if { $zmax > $zmin } {
1135        incr _dim
1136    }
1137    lappend limits xmin $xmin xmax $xmax ymin $ymin ymax $ymax 
1138    set dataAttrs [$dataset GetPointData]
1139    if { $dataAttrs == ""} {
1140        puts stderr "No point data"
1141    }
1142    set vmin 0
1143    set vmax 1
1144    set numArrays [$dataAttrs GetNumberOfArrays]
1145    if { $numArrays > 0 } {
1146        set array [$dataAttrs GetArray 0]
1147        foreach {vmin vmax} [$array GetRange] break
1148
1149        for {set i 0} {$i < [$dataAttrs GetNumberOfArrays] } {incr i} {
1150            set array [$dataAttrs GetArray $i]
1151            set name  [$dataAttrs GetArrayName $i]
1152            foreach {min max} [$array GetRange] break
1153            if { $min < $vmin } {
1154                set vmin $min
1155            }
1156            if { $max > $vmax } {
1157                set vmax $max
1158            }
1159            lappend limits $name-min $min $name-max $max
1160            lappend _fieldNames $name
1161            lappend _fieldUnits ""
1162            lappend _fieldLabels $name
1163        }
1164    }
1165    lappend limits vmin $vmin vmax $vmax
1166    set _comp2limits($cname) $limits
1167    $reader Delete
1168    file delete $tmpfile
1169}
1170
1171#
1172# vtkdata ?<cname>?
1173#
1174#       Returns a string representing the mesh and field data for a specific
1175#       component in basic VTK file format.
1176#
1177itcl::body Rappture::Field::vtkdata {cname} {
1178    if {$cname == "component0"} {
1179        set cname "component"
1180    }
1181    if { [info exists _comp2vtkstreamlines($cname)] } {
1182        return $_comp2vtkstreamlines($cname)
1183    }
1184    if { [info exists _comp2vtkvolume($cname)] } {
1185        return $_comp2vtkvolume($cname)
1186    }
1187    if {[info exists _comp2dx($cname)]} {
1188        return $_comp2dx($cname)
1189    }
1190    if {[info exists _comp2unirect3d($cname)]} {
1191        return [$_comp2unirect3d($cname) blob]
1192    }
1193    if { [info exists _comp2vtkcontour($cname)] && $_dim == 2 } {
1194        return $_comp2vtkcontour($cname)
1195    }
1196    if { [info exists _comp2vtk($cname)] && $_dim == 2 } {
1197        return $_comp2vtk($cname)
1198    }
1199    if { [info exists _comp2heightmap($cname)] } {
1200        set label [hints zlabel]
1201        if { $label == "" } {
1202            set label $cname
1203        } else {
1204            regsub -all { } $label {_} label
1205        }
1206        foreach {mesh vector} $_comp2heightmap($cname) break
1207        append out "# vtk DataFile Version 3.0\n"
1208        append out "[hints label]\n"
1209        append out "ASCII\n"
1210        append out [$mesh vtkdata]
1211        append out "SCALARS $label float\n"
1212        append out "LOOKUP_TABLE default\n"
1213        append out "[$vector range 0 end]\n"
1214        return $out
1215    }
1216    error "can't find vtkdata for $cname. This method should only be called by the vtkheightmap widget"
1217}
1218
1219itcl::body Rappture::Field::BuildPointsOnMesh {cname} {
1220    #
1221    # More complex 2D/3D data is represented by a mesh
1222    # object and an associated vtkFloatArray for field
1223    # values.
1224    #
1225    set path [$_field get $cname.mesh]
1226    if {[$_xmlobj element $path] == ""} {
1227        # Unknown mesh designated.
1228        return
1229    }
1230    set element [$_xmlobj element -as type $path]
1231    lappend _fieldNames $cname
1232    lappend _fieldLabels $cname
1233    lappend _fieldUnits ""
1234    # Handle bizarre cases that hopefully will be deprecated.
1235    if { $element == "unirect3d" } {
1236        if { [$_field element $cname.extents] != "" } {
1237            set extents [$_field get $cname.extents]
1238        } else {
1239            set extents 1
1240        }
1241        set _comp2dims($cname) "3D"
1242        set _comp2unirect3d($cname) \
1243            [Rappture::Unirect3d \#auto $_xmlobj $_field $cname $extents]
1244        set _comp2style($cname) [$_field get $cname.style]
1245        if {[$_field element $cname.flow] != ""} {
1246            set _comp2flowhints($cname) \
1247                [Rappture::FlowHints ::\#auto $_field $cname $_units]
1248        }
1249        incr _counter
1250        return
1251    }
1252    if { $element == "unirect2d" && [$_field element $cname.flow] != "" } {
1253        if { [$_field element $cname.extents] != "" } {
1254            set extents [$_field get $cname.extents]
1255        } else {
1256            set extents 1
1257        }
1258        set _comp2dims($cname) "2D"
1259        set _comp2unirect2d($cname) \
1260            [Rappture::Unirect2d \#auto $_xmlobj $path]
1261        set _comp2style($cname) [$_field get $cname.style]
1262        set _comp2flowhints($cname) \
1263            [Rappture::FlowHints ::\#auto $_field $cname $_units]
1264        set _values [$_field element $cname.values]
1265        incr _counter
1266        return
1267    }
1268    switch -- $element {
1269        "cloud" {
1270            set mesh [Rappture::Cloud::fetch $_xmlobj $path]
1271        }
1272        "mesh" {
1273            set mesh [Rappture::Mesh::fetch $_xmlobj $path]
1274        }           
1275        "unirect2d" {
1276            set mesh [Rappture::Unirect2d::fetch $_xmlobj $path]
1277            set _viewer "vtkheightmap"
1278        }
1279    }
1280    if {[$mesh dimensions] == 1} {
1281        # Is this used anywhere?
1282        #
1283        # OOPS!  This is 1D data
1284        # Forget the cloud/field -- store BLT vectors
1285        #
1286        set xv [blt::vector create x$_counter]
1287        set yv [blt::vector create y$_counter]
1288       
1289        set points [$mesh points]
1290        set max [$points GetNumberOfPoints]
1291        for {set i 0} {$i < $max} {incr i} {
1292            $xv append [lindex [$points GetPoint $i] 0]
1293        }
1294        set class [$mesh info class]
1295        ${class}::release $mesh
1296       
1297        set values [$_field get $cname.values]
1298        foreach y $values {
1299            if {"" != $_units} {
1300                set y [Rappture::Units::convert $y \
1301                           -context $_units -to $_units -units off]
1302            }
1303            $yv append $y
1304        }
1305        # sort x-coords in increasing order
1306        $xv sort $yv
1307       
1308        set _comp2dims($cname) "1D"
1309        set _comp2xy($cname) [list $xv $yv]
1310        incr _counter
1311        return
1312    }
1313
1314    if {[$mesh dimensions] == 3} {
1315        #
1316        # 3D data: Store cloud/field as components
1317        #
1318        set values [$_field get $cname.values]
1319        set farray [vtkFloatArray ::vals$_counter]
1320        foreach v $values {
1321            if {"" != $_units} {
1322                set v [Rappture::Units::convert $v \
1323                           -context $_units -to $_units -units off]
1324            }
1325            $farray InsertNextValue $v
1326        }
1327        set _comp2dims($cname) "[$mesh dimensions]D"
1328        set _comp2vtk($cname) [list $mobj $farray]
1329        set _comp2style($cname) [$_field get $cname.style]
1330        incr _counter
1331        return
1332    }
1333    if {[$mesh dimensions] == 2} {
1334
1335        # 2D data: Convert field data to vtkFloatArray
1336
1337        set vector [blt::vector create \#auto]
1338        $vector set [$_field get $cname.values]
1339        set _comp2dims($cname) "[$mesh dimensions]D"
1340        set _comp2heightmap($cname) [list $mesh $vector]
1341        set _comp2style($cname) [$_field get $cname.style]
1342        incr _counter
1343        set label [hints zlabel]
1344        if { $label != "" } {
1345            set _fieldNames $label
1346            regsub -all { } $_fieldNames {_} _fieldNames
1347            set _fieldLabels $label
1348        }
1349        set units [hints zunits]
1350        if { $units != "" } {
1351            set _fieldUnits $units
1352        }
1353        return
1354    }
1355    error "unhandled case in field"
1356}
Note: See TracBrowser for help on using the repository browser.