source: branches/1.3/gui/scripts/mesh.tcl @ 4918

Last change on this file since 4918 was 4788, checked in by ldelgass, 9 years ago

merge r4787 from trunk

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