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

Last change on this file since 4344 was 4251, checked in by ldelgass, 10 years ago

Merge change from (part of) r4203 on 1.3 branch

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