source: branches/uq/gui/scripts/mesh.tcl @ 4798

Last change on this file since 4798 was 4798, checked in by gah, 10 years ago

sync with former 1.3 branch

File size: 47.9 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2
3# ----------------------------------------------------------------------
4#  COMPONENT: mesh - represents a structured mesh for a device
5#
6#  This object represents a mesh in an XML description of simulator
7#  output.  A mesh is a structured arrangement of points, as elements
8#  composed of nodes representing coordinates.  This is a little
9#  different from a cloud, which is an unstructured arrangement
10#  (shotgun blast) of points.
11# ======================================================================
12#  AUTHOR:  Michael McLennan, Purdue University
13#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
14#
15#  See the file "license.terms" for information on usage and
16#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17# ======================================================================
18package require Itcl
19
20namespace eval Rappture {
21    # forward declaration
22}
23
24itcl::class Rappture::Mesh {
25    private variable _xmlobj ""  ;      # Ref to XML obj with device data
26    private variable _mesh ""    ;      # Lib obj representing this mesh
27    private variable _dim       0;      # Dimension of mesh (1, 2, or 3)
28    private variable _type "";          # Indicates the type of mesh.
29    private variable _axis2units;       # System of units for x, y, z
30    private variable _axis2labels;      #
31    private variable _hints
32    private variable _limits        ;   # Array of mesh limits. Keys are
33                                        # xmin, xmax, ymin, ymax, ...
34    private variable _numPoints 0   ;   # # of points in mesh
35    private variable _numCells 0;       # # of cells in mesh
36    private variable _vtkdata "";       # Mesh in vtk file format.
37    private variable _isValid 0;        # Indicates if the mesh is valid.
38    constructor {xmlobj path} {
39        # defined below
40    }
41    destructor {
42        # defined below
43    }
44    public method points {}
45    public method elements {}
46    public method mesh {{-type "vtk"}}
47    public method size {{what -points}}
48    public method dimensions {}
49    public method limits {which}
50    public method units { axis }
51    public method label { axis }
52    public method hints {{key ""}}
53    public method isvalid {} {
54        return $_isValid
55    }
56    public proc fetch {xmlobj path}
57    public proc release {obj}
58    public method vtkdata {{what -partial}}
59    public method type {} {
60        return $_type
61    }
62    public method numpoints {} {
63        return $_numPoints
64    }
65    public method numcells {} {
66        return $_numCells
67    }
68
69    private common _xp2obj       ;      # used for fetch/release ref counting
70    private common _obj2ref      ;      # used for fetch/release ref counting
71    private variable _xv        ""
72    private variable _yv        ""
73    private variable _zv        ""
74    private variable _xCoords   "";     # For the blt contour only
75    private variable _yCoords   "";     # For the blt contour only
76   
77    private method ReadNodesElements {path}
78    private method GetCellCount { xNum yNum zNum }
79    private method GetDimension { path }
80    private method GetDouble { path }
81    private method GetInt { path }
82    private method InitHints {}
83    private method ReadGrid { path }
84    private method ReadUnstructuredGrid { path }
85    private method ReadVtk { path }
86    private method WriteTriangles { path xv yv zv triangles }
87    private method WriteQuads { path xv yv zv quads }
88    private method WriteVertices { path xv yv zv vertices }
89    private method WriteLines { path xv yv zv lines }
90    private method WritePolygons { path xv yv zv polygons }
91    private method WriteTriangleStrips { path xv yv zv trianglestrips }
92    private method WriteTetrahedrons { path xv yv zv tetrahedrons }
93    private method WriteHexahedrons { path xv yv zv hexhedrons }
94    private method WriteWedges { path xv yv zv wedges }
95    private method WritePyramids { path xv yv zv pyramids }
96    private method WriteHybridCells { path xv yv zv cells celltypes }
97    private method WritePointCloud { path xv yv zv }
98    private method GetCellType { name }
99    private method GetNumIndices { type }
100}
101
102# ----------------------------------------------------------------------
103# USAGE: Rappture::Mesh::fetch <xmlobj> <path>
104#
105# Clients use this instead of a constructor to fetch the Mesh for
106# a particular <path> in the <xmlobj>.  When the client is done with
107# the mesh, he calls "release" to decrement the reference count.
108# When the mesh is no longer needed, it is cleaned up automatically.
109# ----------------------------------------------------------------------
110itcl::body Rappture::Mesh::fetch {xmlobj path} {
111    set handle "$xmlobj|$path"
112    if {[info exists _xp2obj($handle)]} {
113        set obj $_xp2obj($handle)
114        incr _obj2ref($obj)
115        return $obj
116    }
117    set obj [Rappture::Mesh ::#auto $xmlobj $path]
118    set _xp2obj($handle) $obj
119    set _obj2ref($obj) 1
120    return $obj
121}
122
123# ----------------------------------------------------------------------
124# USAGE: Rappture::Mesh::release <obj>
125#
126# Clients call this when they're no longer using a Mesh fetched
127# previously by the "fetch" proc.  This decrements the reference
128# count for the mesh and destroys the object when it is no longer
129# in use.
130# ----------------------------------------------------------------------
131itcl::body Rappture::Mesh::release {obj} {
132    if {[info exists _obj2ref($obj)]} {
133        incr _obj2ref($obj) -1
134        if {$_obj2ref($obj) <= 0} {
135            unset _obj2ref($obj)
136            foreach handle [array names _xp2obj] {
137                if {$_xp2obj($handle) == $obj} {
138                    unset _xp2obj($handle)
139                }
140            }
141            itcl::delete object $obj
142        }
143    } else {
144        error "can't find reference count for $obj"
145    }
146}
147
148# ----------------------------------------------------------------------
149# CONSTRUCTOR
150# ----------------------------------------------------------------------
151itcl::body Rappture::Mesh::constructor {xmlobj path} {
152    package require vtk
153    if {![Rappture::library isvalid $xmlobj]} {
154        error "bad value \"$xmlobj\": should be Rappture::library"
155    }
156    set _xmlobj $xmlobj
157    set _mesh [$xmlobj element -as object $path]
158
159    # Initialize mesh bounds to empty
160    foreach axis {x y z} {
161        set _limits($axis) ""
162    }
163    set units [$_mesh get units]
164    set first [lindex $units 0]
165    foreach u $units axis { x y z } {
166        if { $u != "" } {
167            set _axis2units($axis) $u
168        } else {
169            set _axis2units($axis) $first
170        }
171    }
172    foreach label [$_mesh get labels] axis { x y z } {
173        if { $label != "" } {
174            set _axis2labels($axis) $label
175        } else {
176            set _axis2labels($axis) [string toupper $axis]
177        }
178    }
179
180    # Meshes comes in a variety of flavors
181    #
182    # Dimensionality is determined from the <dimension> tag. 
183    #
184    # <vtk> described mesh
185    # <element> +  <node> definitions
186    # <grid>            rectangular mesh
187    # <unstructured>    homogeneous cell type mesh.
188
189    # Check that only one mesh type was defined.
190    set subcount 0
191    foreach cname [$_mesh children] {
192        foreach type { vtk grid unstructured } {
193            if { $cname == $type } {
194                incr subcount
195                break
196            }
197        }
198    }
199    if {[$_mesh element "node"] != "" ||
200        [$_mesh element "element"] != ""} {
201        incr subcount
202    }
203
204    if { $subcount == 0 } {
205        puts stderr "WARNING: no mesh specified for \"$path\"."
206        return
207    }
208    if { $subcount > 1 } {
209        puts stderr "WARNING: too many mesh types specified for \"$path\"."
210        return
211    }
212    set result 0
213    if { [$_mesh element "vtk"] != ""} {
214        set result [ReadVtk $path]
215    } elseif {[$_mesh element "grid"] != "" } {
216        set result [ReadGrid $path]
217    } elseif {[$_mesh element "unstructured"] != "" } {
218        set result [ReadUnstructuredGrid $path]
219    } elseif {[$_mesh element "node"] != "" && [$_mesh element "element"] != ""} {
220        set result [ReadNodesElements $path]
221    }
222    set _isValid $result
223    InitHints
224}
225
226# ----------------------------------------------------------------------
227# DESTRUCTOR
228# ----------------------------------------------------------------------
229itcl::body Rappture::Mesh::destructor {} {
230    # don't destroy the _xmlobj! we don't own it!
231    itcl::delete object $_mesh
232
233    if { $_xCoords != "" } {
234        blt::vector destroy $_xCoords
235    }
236    if { $_yCoords != "" } {
237        blt::vector destroy $_yCoords
238    }
239}
240
241#
242# vtkdata --
243#
244#       This is called by the field object to generate a VTK file to send to
245#       the remote render server.  Returns the vtkDataSet object containing
246#       (at this point) just the mesh.  The field object doesn't know (or
247#       care) what type of mesh is used.  The field object will add field
248#       arrays before generating output to send to the remote render server.
249#
250itcl::body Rappture::Mesh::vtkdata {{what -partial}} {
251    if {$what == "-full"} {
252        append out "# vtk DataFile Version 3.0\n"
253        append out "[hints label]\n"
254        append out "ASCII\n"
255        append out $_vtkdata
256        return $out
257    } else {
258        return $_vtkdata
259    }
260}
261
262# ----------------------------------------------------------------------
263# USAGE: points
264#
265# Returns the vtk object containing the points for this mesh.
266# ----------------------------------------------------------------------
267itcl::body Rappture::Mesh::points {} {
268    return ""
269}
270
271#
272# units --
273#
274#       Returns the units of the given axis.
275#
276itcl::body Rappture::Mesh::units { axis } {
277    if { ![info exists _axis2units($axis)] } {
278        return ""
279    }
280    return $_axis2units($axis)
281}
282
283#
284# label --
285#
286#       Returns the label of the given axis.
287#
288itcl::body Rappture::Mesh::label { axis } {
289    if { ![info exists _axis2labels($axis)] } {
290        return ""
291    }
292    return $_axis2labels($axis)
293}
294
295# ----------------------------------------------------------------------
296# USAGE: elements
297#
298# Returns a list of the form {plist r plist r ...} for each element
299# in this mesh.  Each plist is a list of {x y x y ...} coordinates
300# for the mesh.
301# ----------------------------------------------------------------------
302itcl::body Rappture::Mesh::elements {} {
303    # build a map for region number => region type
304    foreach comp [$_mesh children -type region] {
305        set id [$_mesh element -as id $comp]
306        set regions($id) [$_mesh get $comp]
307    }
308    set regions() "unknown"
309
310    set rlist ""
311    foreach comp [$_mesh children -type element] {
312        set rid [$_mesh get $comp.region]
313
314        #
315        # HACK ALERT!
316        #
317        # Prophet puts out nodes in a funny "Z" shaped order,
318        # not in proper clockwise fashion.  Switch the last
319        # two nodes for now to make them correct.
320        #
321        set nlist [$_mesh get $comp.nodes]
322        set n2 [lindex $nlist 2]
323        set n3 [lindex $nlist 3]
324        set nlist [lreplace $nlist 2 3 $n3 $n2]
325        lappend nlist [lindex $nlist 0]
326
327        set plist ""
328        foreach nid $nlist {
329            eval lappend plist [$_mesh get node($nid)]
330        }
331        lappend rlist $plist $regions($rid)
332    }
333    return $rlist
334}
335
336# ----------------------------------------------------------------------
337# USAGE: mesh
338#
339# Returns the vtk object representing the mesh.
340# ----------------------------------------------------------------------
341itcl::body Rappture::Mesh::mesh { {type "vtk"} } {
342    switch $type {
343        "vtk" {
344            return ""
345        }
346        default {
347            error "Requested mesh type \"$type\" is unknown."
348        }
349    }
350}
351
352# ----------------------------------------------------------------------
353# USAGE: size ?-points|-elements?
354#
355# Returns the number of points in this mesh.
356# ----------------------------------------------------------------------
357itcl::body Rappture::Mesh::size {{what -points}} {
358    switch -- $what {
359        -points {
360            return $_numPoints
361        }
362        -elements {
363            return $_numCells
364        }
365        default {
366            error "bad option \"$what\": should be -points or -elements"
367        }
368    }
369}
370
371# ----------------------------------------------------------------------
372# USAGE: dimensions
373#
374# Returns the number of dimensions for this object: 1, 2, or 3.
375# ----------------------------------------------------------------------
376itcl::body Rappture::Mesh::dimensions {} {
377    return $_dim
378}
379
380# ----------------------------------------------------------------------
381# USAGE: limits x|y|z
382#
383# Returns the {min max} coords for the limits of the specified axis.
384# ----------------------------------------------------------------------
385itcl::body Rappture::Mesh::limits {axis} {
386    if {![info exists _limits($axis)]} {
387        error "bad axis \"$which\": should be x, y, z"
388    }
389    return $_limits($axis)
390}
391
392# ----------------------------------------------------------------------
393# USAGE: hints ?<keyword>?
394#
395# Returns a list of key/value pairs for various hints about plotting
396# this field.  If a particular <keyword> is specified, then it returns
397# the hint for that <keyword>, if it exists.
398# ----------------------------------------------------------------------
399itcl::body Rappture::Mesh::hints {{keyword ""}} {
400    if {$keyword != ""} {
401        if {[info exists _hints($keyword)]} {
402            return $_hints($keyword)
403        }
404        return ""
405    }
406    return [array get _hints]
407}
408
409# ----------------------------------------------------------------------
410# USAGE: InitHints
411#
412# Returns a list of key/value pairs for various hints about plotting
413# this mesh.  If a particular <keyword> is specified, then it returns
414# the hint for that <keyword>, if it exists.
415# ----------------------------------------------------------------------
416itcl::body Rappture::Mesh::InitHints {} {
417    foreach {key path} {
418        camera       camera.position
419        color        about.color
420        label        about.label
421        style        about.style
422        units        units
423    } {
424        set str [$_mesh get $path]
425        if {"" != $str} {
426            set _hints($key) $str
427        }
428    }
429}
430
431itcl::body Rappture::Mesh::GetDimension { path } {
432    set string [$_xmlobj get $path.dim]
433    if { $string == "" } {
434        puts stderr "WARNING: no tag <dim> found in mesh \"$path\"."
435        return 0
436    }
437    if { [scan $string "%d" _dim] == 1 } {
438        if { $_dim == 1 || $_dim == 2 || $_dim == 3 } {
439            return 1
440        }
441    }
442    puts stderr "WARNING: bad <dim> tag value \"$string\": should be 1, 2 or 3."
443    return 0
444}
445
446itcl::body Rappture::Mesh::GetDouble { path } {
447    set string [$_xmlobj get $path]
448    if { [scan $string "%g" value] != 1 } {
449        puts stderr "ERROR: can't get double value \"$string\" of \"$path\""
450        return 0.0
451    }
452    return $value
453}
454
455itcl::body Rappture::Mesh::GetInt { path } {
456    set string [$_xmlobj get $path]
457    if { [scan $string "%d" value] != 1 } {
458        puts stderr "ERROR: can't get integer value \"$string\" of \"$path\""
459        return 0.0
460    }
461    return $value
462}
463
464itcl::body Rappture::Mesh::ReadVtk { path } {
465    set _type "vtk"
466
467    if { ![GetDimension $path] } {
468        return 0
469    }
470    # Create a VTK file with the mesh in it. 
471    set _vtkdata [$_xmlobj get $path.vtk]
472    append out "# vtk DataFile Version 3.0\n"
473    append out "mesh\n"
474    append out "ASCII\n"
475    append out "$_vtkdata\n"
476
477     # Write the contents to a file just in case it's binary.
478    set tmpfile file[pid].vtk
479    set f [open "$tmpfile" "w"]
480    fconfigure $f -translation binary -encoding binary
481    puts $f $out
482    close $f
483
484    # Read the data back into a vtk dataset and query the bounds.
485    set reader $this-datasetreader
486    vtkDataSetReader $reader
487    $reader SetFileName $tmpfile
488    $reader Update
489    set output [$reader GetOutput]
490    set _numPoints [$output GetNumberOfPoints]
491    set _numCells [$output GetNumberOfCells]
492    foreach { xmin xmax ymin ymax zmin zmax } [$output GetBounds] break
493    set _limits(x) [list $xmin $xmax]
494    set _limits(y) [list $ymin $ymax]
495    set _limits(z) [list $zmin $zmax]
496    file delete $tmpfile
497    rename $output ""
498    rename $reader ""
499    return 1
500}
501
502itcl::body Rappture::Mesh::GetCellCount { xNum yNum zNum } {
503    set numCells 1
504    if { $xNum > 0 } {
505        set numCells [expr $numCells * $xNum]
506    }
507    if { $yNum > 0 } {
508        set numCells [expr $numCells * $yNum]
509    }
510    if { $zNum > 0 } {
511        set numCells [expr $numCells * $zNum]
512    }
513    return $numCells
514}
515
516itcl::body Rappture::Mesh::ReadGrid { path } {
517    set _type "grid"
518
519    if { ![GetDimension $path] } {
520        return 0
521    }
522    set numUniform 0
523    set numRectilinear 0
524    set numCurvilinear 0
525    foreach axis { x y z } {
526        set min    [$_xmlobj get "$path.grid.${axis}axis.min"]
527        set max    [$_xmlobj get "$path.grid.${axis}axis.max"]
528        set num    [$_xmlobj get "$path.grid.${axis}axis.numpoints"]
529        set coords [$_xmlobj get "$path.grid.${axis}coords"]
530        set dim    [$_xmlobj get "$path.grid.${axis}dim"]
531        if { $min != "" && $max != "" && $num != "" && $num > 0 } {
532            set ${axis}Min $min
533            set ${axis}Max $max
534            set ${axis}Num $num
535            if {$min > $max} {
536                puts stderr "ERROR: grid $axis min can't be greater than max"
537                return 0
538            }
539            incr numUniform
540        } elseif { $coords != "" } {
541            incr numRectilinear
542            set ${axis}Coords $coords
543        } elseif { $dim != "" } {
544            set ${axis}Num $dim
545            incr numCurvilinear
546        }
547    }
548    set _dim [expr $numRectilinear + $numUniform + $numCurvilinear]
549    if { $_dim == 0 } {
550        # No data found.
551        puts stderr "WARNING: bad grid \"$path\": no data found"
552        return 0
553    }
554    if { $numCurvilinear > 0 } {
555        # This is the 2D/3D curilinear case. We found <xdim>, <ydim>, or <zdim>
556        if { $numRectilinear > 0 || $numUniform > 0 } {
557            puts stderr "WARNING: bad grid \"$path\": can't mix curvilinear and rectilinear grids."
558            return 0
559        }
560        set points [$_xmlobj get $path.grid.points]
561        if { $points == "" } {
562            puts stderr "WARNING: bad grid \"$path\": no <points> found."
563            return 0
564        }
565        if { ![info exists xNum] } {
566            puts stderr "WARNING: bad grid \"$path\": invalid dimensions for curvilinear grid: missing <xdim> from grid description."
567            return 0
568        }
569        set all [blt::vector create \#auto]
570        set xv [blt::vector create \#auto]
571        set yv [blt::vector create \#auto]
572        set zv [blt::vector create \#auto]
573        $all set $points
574        set numCoords [$all length]
575        if { [info exists zNum] } {
576            set _dim 3
577            set _numPoints [expr $xNum * $yNum * $zNum]
578            set _numCells [GetCellCount $xNum $yNum $zNum]
579            if { ($_numPoints * 3) != $numCoords } {
580                puts stderr "WARNING: bad grid \"$path\": invalid grid: \# of points does not match dimensions <xdim> * <ydim> * <zdim>"
581                return 0
582            }
583            if { ($numCoords % 3) != 0 } {
584                puts stderr "WARNING: bad grid \"$path\": wrong \# of coordinates for 3D grid"
585                return 0
586            }
587            $all split $xv $yv $zv
588            foreach axis {x y z} {
589                set vector [set ${axis}v]
590                set _limits($axis) [$vector limits]
591            }
592            append out "DATASET STRUCTURED_GRID\n"
593            append out "DIMENSIONS $xNum $yNum $zNum\n"
594            append out "POINTS $_numPoints double\n"
595            append out [$all range 0 end]
596            append out "\n"
597            set _vtkdata $out
598        } elseif { [info exists yNum] } {
599            set _dim 2
600            set _numPoints [expr $xNum * $yNum]
601            set _numCells [GetCellCount $xNum $yNum 0]
602            if { ($_numPoints * 2) != $numCoords } {
603                puts stderr "WARNING: bad grid \"$path\": \# of points does not match dimensions <xdim> * <ydim>"
604                return 0
605            }
606            if { ($numCoords % 2) != 0 } {
607                puts stderr "WARNING: bad grid \"$path\": wrong \# of coordinates for 2D grid"
608                return 0
609            }
610            foreach axis {x y} {
611                set vector [set ${axis}v]
612                set _limits($axis) [$vector limits]
613            }
614            set _limits(z) [list 0 0]
615            $zv seq 0 0 [$xv length]
616            $all merge $xv $yv $zv
617            append out "DATASET STRUCTURED_GRID\n"
618            append out "DIMENSIONS $xNum $yNum 1\n"
619            append out "POINTS $_numPoints double\n"
620            append out [$all range 0 end]
621            append out "\n"
622            set _vtkdata $out
623        } else {
624            set _dim 1
625            set _numPoints $xNum
626            set _numCells [GetCellCount $xNum 0 0]
627            if { $_numPoints != $numCoords } {
628                puts stderr "WARNING: bad grid \"$path\": \# of points does not match <xdim>"
629                return 0
630            }
631            set _limits(x) [$xv limits]
632            set _limits(y) [list 0 0]
633            set _limits(z) [list 0 0]
634            $yv seq 0 0 [$xv length]
635            $zv seq 0 0 [$xv length]
636            $all merge $xv $yv $zv
637            append out "DATASET STRUCTURED_GRID\n"
638            append out "DIMENSIONS $xNum 1 1\n"
639            append out "POINTS $_numPoints double\n"
640            append out [$all range 0 end]
641            append out "\n"
642            set _vtkdata $out
643        }
644        blt::vector destroy $all $xv $yv $zv
645        return 1
646    }
647    if { $numRectilinear == 0 && $numUniform > 0} {
648        # This is the special case where all axes 2D/3D are uniform. 
649        # This results in a STRUCTURED_POINTS
650        if { $_dim == 1 } {
651            set xSpacing 0
652            if { $xNum > 1 } {
653                set xSpacing [expr ($xMax - $xMin) / double($xNum - 1)]
654            }
655            set _numPoints $xNum
656            set _numCells [GetCellCount $xNum 0 0]
657            append out "DATASET STRUCTURED_POINTS\n"
658            append out "DIMENSIONS $xNum 1 1\n"
659            append out "ORIGIN $xMin 0 0\n"
660            append out "SPACING $xSpacing 0 0\n"
661            set _vtkdata $out
662            set _limits(x) [list $xMin $xMax]
663            set _limits(y) [list 0 0]
664            set _limits(z) [list 0 0]
665        } elseif { $_dim == 2 } {
666            set xSpacing 0
667            set ySpacing 0
668            if { $xNum > 1 } {
669                set xSpacing [expr ($xMax - $xMin) / double($xNum - 1)]
670            }
671            if { $yNum > 1 } {
672                set ySpacing [expr ($yMax - $yMin) / double($yNum - 1)]
673            }
674            set _numPoints [expr $xNum * $yNum]
675            set _numCells [GetCellCount $xNum $yNum 0]
676            append out "DATASET STRUCTURED_POINTS\n"
677            append out "DIMENSIONS $xNum $yNum 1\n"
678            append out "ORIGIN $xMin $yMin 0\n"
679            append out "SPACING $xSpacing $ySpacing 0\n"
680            set _vtkdata $out
681            foreach axis {x y} {
682                set _limits($axis) [list [set ${axis}Min] [set ${axis}Max]]
683            }
684            set _limits(z) [list 0 0]
685        } elseif { $_dim == 3 } {
686            set xSpacing 0
687            set ySpacing 0
688            set zSpacing 0
689            if {$xNum > 1} {
690                set xSpacing [expr ($xMax - $xMin) / double($xNum - 1)]
691            }
692            if {$yNum > 1} {
693                set ySpacing [expr ($yMax - $yMin) / double($yNum - 1)]
694            }
695            if {$zNum > 1} {
696                set zSpacing [expr ($zMax - $zMin) / double($zNum - 1)]
697            }
698            set _numPoints [expr $xNum * $yNum * $zNum]
699            set _numCells [GetCellCount $xNum $yNum $zNum]
700            append out "DATASET STRUCTURED_POINTS\n"
701            append out "DIMENSIONS $xNum $yNum $zNum\n"
702            append out "ORIGIN $xMin $yMin $zMin\n"
703            append out "SPACING $xSpacing $ySpacing $zSpacing\n"
704            set _vtkdata $out
705            foreach axis {x y z} {
706                set _limits($axis) [list [set ${axis}Min] [set ${axis}Max]]
707            }
708        } else {
709            puts stderr "WARNING: bad grid \"$path\": bad dimension \"$_dim\""
710            return 0
711        }
712        return 1
713    }
714    # This is the hybrid case.  Some axes are uniform, others are nonuniform.
715    set xv [blt::vector create \#auto]
716    if { [info exists xMin] } {
717        $xv seq $xMin $xMax $xNum
718    } else {
719        $xv set [$_xmlobj get $path.grid.xcoords]
720        set xMin [$xv min]
721        set xMax [$xv max]
722        set xNum [$xv length]
723    }
724    set yv [blt::vector create \#auto]
725    if { $_dim > 1 } {
726        if { [info exists yMin] } {
727            $yv seq $yMin $yMax $yNum
728        } else {
729            $yv set [$_xmlobj get $path.grid.ycoords]
730            set yMin [$yv min]
731            set yMax [$yv max]
732            set yNum [$yv length]
733        }
734    } else {
735        set yNum 1
736    }
737    set zv [blt::vector create \#auto]
738    if { $_dim == 3 } {
739        if { [info exists zMin] } {
740            $zv seq $zMin $zMax $zNum
741        }  else {
742            $zv set [$_xmlobj get $path.grid.zcoords]
743            set zMin [$zv min]
744            set zMax [$zv max]
745            set zNum [$zv length]
746        }
747    } else {
748        set zNum 1
749    }
750    if { $_dim == 3 } {
751        set _numPoints [expr $xNum * $yNum * $zNum]
752        set _numCells [GetCellCount $xNum $yNum $zNum]
753        append out "DATASET RECTILINEAR_GRID\n"
754        append out "DIMENSIONS $xNum $yNum $zNum\n"
755        append out "X_COORDINATES $xNum double\n"
756        append out [$xv range 0 end]
757        append out "\n"
758        append out "Y_COORDINATES $yNum double\n"
759        append out [$yv range 0 end]
760        append out "\n"
761        append out "Z_COORDINATES $zNum double\n"
762        append out [$zv range 0 end]
763        append out "\n"
764        set _vtkdata $out
765        foreach axis {x y z} {
766            if { [info exists ${axis}Min] } {
767                set _limits($axis) [list [set ${axis}Min] [set ${axis}Max]]
768            }
769        }
770    } elseif { $_dim == 2 } {
771        set _numPoints [expr $xNum * $yNum]
772        set _numCells [GetCellCount $xNum $yNum 0]
773        append out "DATASET RECTILINEAR_GRID\n"
774        append out "DIMENSIONS $xNum $yNum 1\n"
775        append out "X_COORDINATES $xNum double\n"
776        append out [$xv range 0 end]
777        append out "\n"
778        append out "Y_COORDINATES $yNum double\n"
779        append out [$yv range 0 end]
780        append out "\n"
781        append out "Z_COORDINATES 1 double\n"
782        append out "0\n"
783        foreach axis {x y} {
784            if { [info exists ${axis}Min] } {
785                set _limits($axis) [list [set ${axis}Min] [set ${axis}Max]]
786            }
787        }
788        set _limits(z) [list 0 0]
789        set _vtkdata $out
790    } elseif { $_dim == 1 } {
791        set _numPoints $xNum
792        set _numCells [GetCellCount $xNum 0 0]
793        append out "DATASET RECTILINEAR_GRID\n"
794        append out "DIMENSIONS $xNum 1 1\n"
795        append out "X_COORDINATES $xNum double\n"
796        append out [$xv range 0 end]
797        append out "\n"
798        append out "Y_COORDINATES 1 double\n"
799        append out "0\n"
800        append out "Z_COORDINATES 1 double\n"
801        append out "0\n"
802        if { [info exists xMin] } {
803            set _limits(x) [list $xMin $xMax]
804        }
805        set _limits(y) [list 0 0]
806        set _limits(z) [list 0 0]
807        set _vtkdata $out
808    } else {
809        puts stderr "WARNING: bad grid \"$path\": invalid dimension \"$_dim\""
810        return 0
811    }
812    blt::vector destroy $xv $yv $zv
813    return 1
814}
815
816itcl::body Rappture::Mesh::WritePointCloud { path xv yv zv } {
817    set _type "cloud"
818    set _numPoints [$xv length]
819    append out "DATASET POLYDATA\n"
820    append out "POINTS $_numPoints double\n"
821    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
822        append out "$x $y $z\n"
823    }
824    set _vtkdata $out
825    set _limits(x) [$xv limits]
826    if { $_dim > 1 } {
827        set _limits(y) [$yv limits]
828    } else {
829        set _limits(y) [list 0 0]
830    }
831    if { $_dim == 3 } {
832        set _limits(z) [$zv limits]
833    } else {
834        set _limits(z) [list 0 0]
835    }
836    return 1
837}
838
839itcl::body Rappture::Mesh::WriteTriangles { path xv yv zv triangles } {
840    set _type "triangles"
841    set _numPoints [$xv length]
842    set _numCells 0
843    set data {}
844    set celltypes {}
845    foreach { a b c } $triangles {
846        append data " 3 $a $b $c\n"
847        append celltypes "5\n"
848        incr _numCells
849    }
850    append out "DATASET UNSTRUCTURED_GRID\n"
851    append out "POINTS $_numPoints double\n"
852    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
853        append out " $x $y $z\n"
854    }
855    set count [expr $_numCells * 4]
856    append out "CELLS $_numCells $count\n"
857    append out $data
858    append out "CELL_TYPES $_numCells\n"
859    append out $celltypes
860    set _limits(x) [$xv limits]
861    set _limits(y) [$yv limits]
862    if { $_dim == 3 } {
863        set _limits(z) [$zv limits]
864    } else {
865        set _limits(z) [list 0 0]
866    }
867    set _vtkdata $out
868    return 1
869}
870
871itcl::body Rappture::Mesh::WriteQuads { path xv yv zv quads } {
872    set _type "quads"
873    set _numPoints [$xv length]
874    set _numCells 0
875    set data {}
876    set celltypes {}
877    foreach { a b c d } $quads {
878        append data " 4 $a $b $c $d\n"
879        append celltypes "9\n"
880        incr _numCells
881    }
882    append out "DATASET UNSTRUCTURED_GRID\n"
883    append out "POINTS $_numPoints double\n"
884    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
885        append out " $x $y $z\n"
886    }
887    set count [expr $_numCells * 5]
888    append out "CELLS $_numCells $count\n"
889    append out $data
890    append out "CELL_TYPES $_numCells\n"
891    append out $celltypes
892    set _limits(x) [$xv limits]
893    set _limits(y) [$yv limits]
894    if { $_dim == 3 } {
895        set _limits(z) [$zv limits]
896    } else {
897        set _limits(z) [list 0 0]
898    }
899    set _vtkdata $out
900    return 1
901}
902
903itcl::body Rappture::Mesh::WriteVertices { path xv yv zv vertices } {
904    set _type "vertices"
905    set _numPoints [$xv length]
906    set _numCells 0
907    set data {}
908    set lines [split $vertices \n]
909    set count 0
910    foreach { line } $lines {
911        set numIndices [llength $line]
912        if { $numIndices == 0 } {
913            continue
914        }
915        append data " $numIndices $line\n"
916        incr _numCells
917        set count [expr $count + $numIndices + 1]
918    }
919    append out "DATASET POLYDATA\n"
920    append out "POINTS $_numPoints double\n"
921    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
922        append out " $x $y $z\n"
923    }
924    append out "VERTICES $_numCells $count\n"
925    append out $data
926    set _limits(x) [$xv limits]
927    set _limits(y) [$yv limits]
928    if { $_dim == 3 } {
929        set _limits(z) [$zv limits]
930    } else {
931        set _limits(z) [list 0 0]
932    }
933    set _vtkdata $out
934    return 1
935}
936
937itcl::body Rappture::Mesh::WriteLines { path xv yv zv polylines } {
938    set _type "lines"
939    set _numPoints [$xv length]
940    set _numCells 0
941    set data {}
942    set lines [split $polylines \n]
943    set count 0
944    foreach { line } $lines {
945        set numIndices [llength $line]
946        if { $numIndices == 0 } {
947            continue
948        }
949        append data " $numIndices $line\n"
950        incr _numCells
951        set count [expr $count + $numIndices + 1]
952    }
953    append out "DATASET POLYDATA\n"
954    append out "POINTS $_numPoints double\n"
955    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
956        append out " $x $y $z\n"
957    }
958    append out "LINES $_numCells $count\n"
959    append out $data
960    set _limits(x) [$xv limits]
961    set _limits(y) [$yv limits]
962    if { $_dim == 3 } {
963        set _limits(z) [$zv limits]
964    } else {
965        set _limits(z) [list 0 0]
966    }
967    set _vtkdata $out
968    return 1
969}
970
971itcl::body Rappture::Mesh::WritePolygons { path xv yv zv polygons } {
972    set _type "polygons"
973    set _numPoints [$xv length]
974    set _numCells 0
975    set data {}
976    set lines [split $polygons \n]
977    set count 0
978    foreach { line } $lines {
979        set numIndices [llength $line]
980        if { $numIndices == 0 } {
981            continue
982        }
983        append data " $numIndices $line\n"
984        incr _numCells
985        set count [expr $count + $numIndices + 1]
986    }
987    append out "DATASET POLYDATA\n"
988    append out "POINTS $_numPoints double\n"
989    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
990        append out " $x $y $z\n"
991    }
992    append out "POLYGONS $_numCells $count\n"
993    append out $data
994    set _limits(x) [$xv limits]
995    set _limits(y) [$yv limits]
996    if { $_dim == 3 } {
997        set _limits(z) [$zv limits]
998    } else {
999        set _limits(z) [list 0 0]
1000    }
1001    set _vtkdata $out
1002    return 1
1003}
1004
1005itcl::body Rappture::Mesh::WriteTriangleStrips { path xv yv zv trianglestrips } {
1006    set _type "trianglestrips"
1007    set _numPoints [$xv length]
1008    set _numCells 0
1009    set data {}
1010    set lines [split $trianglestrips \n]
1011    set count 0
1012    foreach { line } $lines {
1013        set numIndices [llength $line]
1014        if { $numIndices == 0 } {
1015            continue
1016        }
1017        append data " $numIndices $line\n"
1018        incr _numCells
1019        set count [expr $count + $numIndices + 1]
1020    }
1021    append out "DATASET POLYDATA\n"
1022    append out "POINTS $_numPoints double\n"
1023    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
1024        append out " $x $y $z\n"
1025    }
1026    append out "TRIANGLE_STRIPS $_numCells $count\n"
1027    append out $data
1028    set _limits(x) [$xv limits]
1029    set _limits(y) [$yv limits]
1030    if { $_dim == 3 } {
1031        set _limits(z) [$zv limits]
1032    } else {
1033        set _limits(z) [list 0 0]
1034    }
1035    set _vtkdata $out
1036    return 1
1037}
1038
1039itcl::body Rappture::Mesh::WriteTetrahedrons { path xv yv zv tetras } {
1040    set _type "tetrahedrons"
1041    set _numPoints [$xv length]
1042    set _numCells 0
1043    set data {}
1044    set celltypes {}
1045    foreach { a b c d } $tetras {
1046        append data " 4 $a $b $c $d\n"
1047        append celltypes "10\n"
1048        incr _numCells
1049    }
1050    append out "DATASET UNSTRUCTURED_GRID\n"
1051    append out "POINTS $_numPoints double\n"
1052    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
1053        append out " $x $y $z\n"
1054    }
1055    set count [expr $_numCells * 5]
1056    append out "CELLS $_numCells $count\n"
1057    append out $data
1058    append out "CELL_TYPES $_numCells\n"
1059    append out $celltypes
1060    set _limits(x) [$xv limits]
1061    set _limits(y) [$yv limits]
1062    set _limits(z) [$zv limits]
1063
1064    set _vtkdata $out
1065    return 1
1066}
1067
1068itcl::body Rappture::Mesh::WriteHexahedrons { path xv yv zv hexas } {
1069    set _type "hexahedrons"
1070    set _numPoints [$xv length]
1071    set _numCells 0
1072    set data {}
1073    set celltypes {}
1074    foreach { a b c d e f g h } $hexas {
1075        append data " 8 $a $b $c $d $e $f $g $h\n"
1076        append celltypes "12\n"
1077        incr _numCells
1078    }
1079    append out "DATASET UNSTRUCTURED_GRID\n"
1080    append out "POINTS $_numPoints double\n"
1081    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
1082        append out " $x $y $z\n"
1083    }
1084    set count [expr $_numCells * 9]
1085    append out "CELLS $_numCells $count\n"
1086    append out $data
1087    append out "CELL_TYPES $_numCells\n"
1088    append out $celltypes
1089    set _limits(x) [$xv limits]
1090    set _limits(y) [$yv limits]
1091    set _limits(z) [$zv limits]
1092
1093    set _vtkdata $out
1094    return 1
1095}
1096
1097itcl::body Rappture::Mesh::WriteWedges { path xv yv zv wedges } {
1098    set _type "wedges"
1099    set _numPoints [$xv length]
1100    set _numCells 0
1101    set data {}
1102    set celltypes {}
1103    foreach { a b c d e f } $wedges {
1104        append data " 6 $a $b $c $d $e $f\n"
1105        append celltypes "13\n"
1106        incr _numCells
1107    }
1108    append out "DATASET UNSTRUCTURED_GRID\n"
1109    append out "POINTS $_numPoints double\n"
1110    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
1111        append out " $x $y $z\n"
1112    }
1113    set count [expr $_numCells * 7]
1114    append out "CELLS $_numCells $count\n"
1115    append out $data
1116    append out "CELL_TYPES $_numCells\n"
1117    append out $celltypes
1118    set _limits(x) [$xv limits]
1119    set _limits(y) [$yv limits]
1120    set _limits(z) [$zv limits]
1121
1122    set _vtkdata $out
1123    return 1
1124}
1125
1126itcl::body Rappture::Mesh::WritePyramids { path xv yv zv pyramids } {
1127    set _type "pyramids"
1128    set _numPoints [$xv length]
1129    set _numCells 0
1130    set data {}
1131    set celltypes {}
1132    foreach { a b c d e } $pyramids {
1133        append data " 5 $a $b $c $d $e\n"
1134        append celltypes "14\n"
1135        incr _numCells
1136    }
1137    append out "DATASET UNSTRUCTURED_GRID\n"
1138    append out "POINTS $_numPoints double\n"
1139    foreach x [$xv range 0 end] y [$yv range 0 end] z [$zv range 0 end] {
1140        append out " $x $y $z\n"
1141    }
1142    set count [expr $_numCells * 6]
1143    append out "CELLS $_numCells $count\n"
1144    append out $data
1145    append out "CELL_TYPES $_numCells\n"
1146    append out $celltypes
1147    set _limits(x) [$xv limits]
1148    set _limits(y) [$yv limits]
1149    set _limits(z) [$zv limits]
1150
1151    set _vtkdata $out
1152    return 1
1153}
1154
1155itcl::body Rappture::Mesh::WriteHybridCells { path xv yv zv cells celltypes } {
1156    set _type "unstructured"
1157    set lines [split $cells \n]
1158    set numCellTypes [llength $celltypes]
1159    if { $numCellTypes == 1 } {
1160        set celltype [GetCellType $celltypes]
1161    }
1162
1163    set _numPoints [$xv length]
1164    set data {}
1165    set count 0
1166    set _numCells 0
1167    set celltypes {}
1168    foreach line $lines {
1169        set length [llength $line]
1170        if { $length == 0 } {
1171            continue
1172        }
1173        if { $numCellTypes > 1 } {
1174            set cellType [GetCellType [lindex $cellTypes $_numCells]]
1175        }
1176        set numIndices [GetNumIndices $celltype]
1177        if { $numIndices > 0 && $numIndices != $length } {
1178            puts stderr "WARNING: bad unstructured grid \"$path\": wrong \# of indices specified for celltype $celltype on line \"$line\""
1179            return 0
1180        } else {
1181            set numIndices $length
1182        }
1183        append data " $numIndices $line\n"
1184        lappend celltypes $celltype
1185        incr count $length;         # Include the indices
1186        incr count;                 # and the number of indices
1187        incr _numCells
1188    }
1189    append out "DATASET UNSTRUCTURED_GRID\n"
1190    append out "POINTS $_numPoints double\n"
1191    set all [blt::vector create \#auto]
1192    $all merge $xv $yv $zv
1193    append out [$all range 0 end]
1194    blt::vector destroy $all
1195    append out "CELLS $_numCells $count\n"
1196    append out $data
1197    append out "CELL_TYPES $_numCells\n"
1198    append out $celltypes
1199    set _limits(x) [$xv limits]
1200    set _limits(y) [$yv limits]
1201    if { $_dim < 3 } {
1202        set _limits(z) [list 0 0]
1203    } else {
1204        set _limits(z) [$zv limits]
1205    }
1206
1207    set _vtkdata $out
1208    return 1
1209}
1210
1211itcl::body Rappture::Mesh::ReadUnstructuredGrid { path } {
1212    set _type "unstructured"
1213
1214    if { ![GetDimension $path] } {
1215        return 0
1216    }
1217    # Step 1: Verify that there's only one cell tag of any kind.
1218    set numCells 0
1219    foreach type {
1220        cells
1221        hexahedrons
1222        lines
1223        polygons
1224        pyramids
1225        quads
1226        tetrahedrons
1227        triangles
1228        trianglestrips
1229        vertices
1230        wedges
1231    } {
1232        set data [$_xmlobj get $path.unstructured.$type]
1233        if { $data != "" } {
1234            incr numCells
1235        }
1236    }
1237    # The generic <cells> tag requires there be a <celltypes> tag too.
1238    set celltypes [$_xmlobj get $path.unstructured.celltypes]
1239    if { $numCells == 0 && $celltypes != "" } {
1240        puts stderr "WARNING: bad unstuctured grid \"$path\": no <cells> description found."
1241        return 0
1242    }
1243    if { $numCells > 1 } {
1244        puts stderr "WARNING: bad unstructured grid \"$path\": too many <cells>, <triangles>, <quads>... descriptions found: should be only one."
1245        return 0
1246    }
1247    foreach type { cells
1248        vertices lines polygons trianglestrips
1249        triangles quads
1250        tetrahedrons hexahedrons wedges pyramids } {
1251        set data [$_xmlobj get $path.unstructured.$type]
1252        if { $data != "" } {
1253            break
1254        }
1255    }
1256    # Step 2: Allow points to be specified as <points> or
1257    #         <xcoords>, <ycoords>, <zcoords>.  Split and convert into
1258    #         3 vectors, one for each coordinate.
1259    if { $_dim == 1 } {
1260        set xcoords [$_xmlobj get $path.unstructured.xcoords]
1261        set ycoords [$_xmlobj get $path.unstructured.ycoords]
1262        set zcoords [$_xmlobj get $path.unstructured.zcoords]
1263        set data    [$_xmlobj get $path.unstructured.points]
1264        if { $ycoords != "" } {
1265            put stderr "can't specify <ycoords> with a 1D mesh"
1266            return 0
1267        }
1268        if { $zcoords != "" } {
1269            put stderr "can't specify <zcoords> with a 1D mesh"
1270            return 0
1271        }
1272        if { $xcoords != "" } {
1273            set xv [blt::vector create \#auto]
1274            $xv set $xcoords
1275        } elseif { $data != "" } {
1276            Rappture::ReadPoints $data dim points
1277            if { $points == "" } {
1278                puts stderr "WARNING: bad unstructured grid \"$path\": no <points> found."
1279                return 0
1280            }
1281            if { $dim != 1 } {
1282                puts stderr "WARNING: bad unstructured grid \"$path\": \# of coordinates per point is \"$dim\": does not agree with dimension specified for mesh \"$_dim\""
1283                return 0
1284            }
1285            set xv [blt::vector create \#auto]
1286            $xv set $points
1287        } else {
1288            puts stderr "WARNING: bad unstructured grid \"$path\": no points specified."
1289            return 0
1290        }
1291        set yv [blt::vector create \#auto]
1292        set zv [blt::vector create \#auto]
1293        $yv seq 0 0 [$xv length];       # Make an all zeroes vector.
1294        $zv seq 0 0 [$xv length];       # Make an all zeroes vector.
1295    } elseif { $_dim == 2 } {
1296        set xcoords [$_xmlobj get $path.unstructured.xcoords]
1297        set ycoords [$_xmlobj get $path.unstructured.ycoords]
1298        set zcoords [$_xmlobj get $path.unstructured.zcoords]
1299        set data    [$_xmlobj get $path.unstructured.points]
1300        if { $zcoords != "" } {
1301            put stderr "can't specify <zcoords> with a 2D mesh"
1302            return 0
1303        }
1304        if { $xcoords != "" && $ycoords != "" } {
1305            set xv [blt::vector create \#auto]
1306            set yv [blt::vector create \#auto]
1307            $xv set $xcoords
1308            $yv set $ycoords
1309        } elseif { $data != "" } {
1310            Rappture::ReadPoints $data dim points
1311            if { $points == "" } {
1312                puts stderr "WARNING: bad unstructured grid \"$path\": no <points> found."
1313                return 0
1314            }
1315            if { $dim != 2 } {
1316                puts stderr "WARNING: bad unstructured grid \"$path\": \# of coordinates per point is \"$dim\": does not agree with dimension specified for mesh \"$_dim\""
1317                return 0
1318            }
1319            set all [blt::vector create \#auto]
1320            set xv [blt::vector create \#auto]
1321            set yv [blt::vector create \#auto]
1322            $all set $points
1323            $all split $xv $yv
1324            blt::vector destroy $all
1325        } else {
1326            puts stderr "WARNING: bad unstructured grid \"$path\": no points specified."
1327            return 0
1328        }
1329        set zv [blt::vector create \#auto]
1330        $zv seq 0 0 [$xv length];       # Make an all zeroes vector.
1331    } elseif { $_dim == 3 } {
1332        set xcoords [$_xmlobj get $path.unstructured.xcoords]
1333        set ycoords [$_xmlobj get $path.unstructured.ycoords]
1334        set zcoords [$_xmlobj get $path.unstructured.zcoords]
1335        set data    [$_xmlobj get $path.unstructured.points]
1336        if { $xcoords != "" && $ycoords != "" && $zcoords != "" } {
1337            set xv [blt::vector create \#auto]
1338            set yv [blt::vector create \#auto]
1339            set zv [blt::vector create \#auto]
1340            $xv set $xcoords
1341            $yv set $ycoords
1342            $zv set $zcoords
1343        } elseif { $data != "" }  {
1344            Rappture::ReadPoints $data dim points
1345            if { $points == "" } {
1346                puts stderr "WARNING: bad unstructured grid \"$path\": no <points> found."
1347                return 0
1348            }
1349            if { $dim != 3 } {
1350                puts stderr "WARNING: bad unstructured grid \"$path\": \# of coordinates per point is \"$dim\": does not agree with dimension specified for mesh \"$_dim\""
1351                return 0
1352            }
1353            set all [blt::vector create \#auto]
1354            set xv [blt::vector create \#auto]
1355            set yv [blt::vector create \#auto]
1356            set zv [blt::vector create \#auto]
1357            $all set $points
1358            $all split $xv $yv $zv
1359            blt::vector destroy $all
1360        } else {
1361            puts stderr "WARNING: bad unstructured grid \"$path\": no points specified."
1362            return 0
1363        }
1364    }
1365    set _numPoints [$xv length]
1366
1367    # Step 3: Write the points and cells as vtk data.
1368    if { $numCells == 0 } {
1369        set result [WritePointCloud $path $xv $yv $zv]
1370    } elseif { $type == "cells" } {
1371        set cells [$_xmlobj get $path.unstructured.cells]
1372        if { $cells == "" } {
1373            puts stderr "WARNING: bad unstructured grid \"$path\": no cells found."
1374            return 0
1375        }
1376        set result [WriteHybridCells $path $xv $yv $zv $cells $celltypes]
1377    } else {
1378        set cmd "Write[string totitle $type]"
1379        set cells [$_xmlobj get $path.unstructured.$type]
1380        if { $cells == "" } {
1381            puts stderr "WARNING: bad unstructured grid \"$path\": no cells found."
1382            return 0
1383        }
1384        set result [$cmd $path $xv $yv $zv $cells]
1385    }
1386    # Clean up.
1387    blt::vector destroy $xv $yv $zv
1388    return $result
1389}
1390
1391# ----------------------------------------------------------------------
1392# USAGE: ReadNodesElements <path>
1393#
1394# Used internally to build a mesh representation based on nodes and
1395# elements stored in the XML.
1396# ----------------------------------------------------------------------
1397itcl::body Rappture::Mesh::ReadNodesElements {path} {
1398    set _type "nodeselements"
1399    set count 0
1400
1401    # Scan for nodes.  Each node represents a vertex.
1402    set data {}
1403    foreach cname [$_xmlobj children -type node $path] {
1404        append data "[$_xmlobj get $path.$cname]\n"
1405    }   
1406    Rappture::ReadPoints $data _dim points
1407    if { $_dim == 2 } {
1408        set all [blt::vector create \#auto]
1409        set xv [blt::vector create \#auto]
1410        set yv [blt::vector create \#auto]
1411        set zv [blt::vector create \#auto]
1412        $all set $points
1413        $all split $xv $yv
1414        set _numPoints [$xv length]
1415        set _limits(x) [$xv limits]
1416        set _limits(y) [$yv limits]
1417        set _limits(z) [list 0 0]
1418        # 2D Dataset. All Z coordinates are 0
1419        $zv seq 0.0 0.0 $_numPoints
1420        $all merge $xv $yv $zv
1421        set points [$all range 0 end]
1422        blt::vector destroy $all $xv $yv $zv
1423    } elseif { $_dim == 3 } {
1424        set all [blt::vector create \#auto]
1425        set xv [blt::vector create \#auto]
1426        set yv [blt::vector create \#auto]
1427        set zv [blt::vector create \#auto]
1428        $all set $points
1429        $all split $xv $yv $zv
1430        set _numPoints [$xv length]
1431        set _limits(x) [$xv limits]
1432        set _limits(y) [$yv limits]
1433        set _limits(z) [$zv limits]
1434        set points [$all range 0 end]
1435        blt::vector destroy $all $xv $yv $zv
1436    } else {
1437        error "bad dimension \"$_dim\" for nodes mesh"
1438    }
1439    array set node2celltype {
1440        3 5
1441        4 10
1442        8 12
1443        6 13
1444        5 14
1445    }
1446    set count 0
1447    set _numCells 0
1448    set celltypes {}
1449    set data {}
1450    # Next scan for elements.  Each element represents a cell.
1451    foreach cname [$_xmlobj children -type element $path] {
1452        set nodeList [$_mesh get $cname.nodes]
1453        set numNodes [llength $nodeList]
1454        if { ![info exists node2celltype($numNodes)] } {
1455            puts stderr "WARNING: bad nodes/elements mesh \$path\": unknown number of indices \"$_numNodes\": should be 3, 4, 5, 6, or 8"
1456            return 0
1457        }
1458        set celltype $node2celltype($numNodes)
1459        append celltypes "  $celltype\n"
1460        if { $celltype == 12 } {
1461            # Formerly used voxels instead of hexahedrons. We're converting
1462            # it here to be backward compatible and still fault-tolerant of
1463            # non-axis aligned cells.
1464            set newList {}
1465            foreach i { 0 1 3 2 4 5 7 6 } {
1466                lappend newList [lindex $nodeList $i]
1467            }
1468            set nodeList $newList
1469        }
1470        append data "  $numNodes $nodeList\n"
1471        incr _numCells
1472        incr count $numNodes
1473        incr count;                     # One extra for the VTK celltype id.
1474    }
1475
1476    append out "DATASET UNSTRUCTURED_GRID\n"
1477    append out "POINTS $_numPoints double\n"
1478    append out $points
1479    append out "\n"
1480    append out "CELLS $_numCells $count\n"
1481    append out $data
1482    append out "CELL_TYPES $_numCells\n"
1483    append out $celltypes
1484    append out "\n"
1485    set _vtkdata $out
1486    set _isValid 1
1487}
1488
1489itcl::body Rappture::Mesh::GetCellType { name } {
1490    array set name2type {
1491        "vertex"          1
1492        "polyvertex"      2
1493        "line"            3
1494        "polyline"        4
1495        "triangle"        5
1496        "trianglestrip"   6
1497        "polygon"         7
1498        "pixel"           8
1499        "quad"            9
1500        "tetrahedron"     10
1501        "voxel"           11
1502        "hexahedron"      12
1503        "wedge"           13
1504        "pyramid"         14
1505        "pentagonalprism" 15
1506        "hexagonalprism"  16
1507    }
1508    if { [info exists name2type($name)] } {
1509        return $name2type($name)
1510    }
1511    if { [string is int $name] == 1 && $name >= 1 && $name <= 16 } {
1512        return $name
1513    }
1514    error "invalid celltype \"$name\""
1515}
1516
1517itcl::body Rappture::Mesh::GetNumIndices { type } {
1518    array set type2indices {
1519        1       1
1520        2       0
1521        3       2
1522        4       0
1523        5       3
1524        6       0
1525        7       0
1526        8       4
1527        9       4
1528        10      4
1529        11      8
1530        12      8
1531        13      6
1532        14      5
1533        15      10
1534        16      12
1535    }
1536    if { [info exists type2indices($type)] } {
1537        return $type2indices($type)
1538    }
1539    error "invalid celltype \"$type\""
1540}
Note: See TracBrowser for help on using the repository browser.