source: trunk/gui/scripts/mesh.tcl @ 5975

Last change on this file since 5975 was 5659, checked in by ldelgass, 9 years ago

whitespace

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