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

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

move "package require vtk" into constructor so that applications do not have to load vtk to use Rappture widgets

File size: 12.6 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: mesh - represents a structured mesh for a device
3#
4#  This object represents a mesh in an XML description of simulator
5#  output.  A mesh is a structured arrangement of points, as elements
6#  composed of nodes representing coordinates.  This is a little
7#  different from a cloud, which is an unstructured arrangement
8#  (shotgun blast) of points.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2005  Purdue Research Foundation
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require Itcl
17
18namespace eval Rappture { # forward declaration }
19
20itcl::class Rappture::Mesh {
21    constructor {xmlobj path} { # defined below }
22    destructor { # defined below }
23
24    public method points {}
25    public method elements {}
26    public method mesh {}
27    public method size {{what -points}}
28    public method dimensions {}
29    public method limits {which}
30    public method hints {{key ""}}
31
32    public proc fetch {xmlobj path}
33    public proc release {obj}
34
35    private method _buildNodesElements {xmlobj path}
36    private method _buildRectMesh {xmlobj path}
37    private method _getVtkElement {npts}
38
39    private variable _xmlobj ""  ;# ref to XML obj with device data
40    private variable _mesh ""    ;# lib obj representing this mesh
41    private variable _pts2elem   ;# maps # points => vtk element
42
43    private variable _units "m m m" ;# system of units for x, y, z
44    private variable _limits        ;# limits xmin, xmax, ymin, ymax, ...
45    private variable _pdata ""      ;# name of vtkPointData object
46    private variable _gdata ""      ;# name of vtkDataSet object
47
48    private common _xp2obj       ;# used for fetch/release ref counting
49    private common _obj2ref      ;# used for fetch/release ref counting
50}
51
52# ----------------------------------------------------------------------
53# USAGE: Rappture::Mesh::fetch <xmlobj> <path>
54#
55# Clients use this instead of a constructor to fetch the Mesh for
56# a particular <path> in the <xmlobj>.  When the client is done with
57# the mesh, he calls "release" to decrement the reference count.
58# When the mesh is no longer needed, it is cleaned up automatically.
59# ----------------------------------------------------------------------
60itcl::body Rappture::Mesh::fetch {xmlobj path} {
61    set handle "$xmlobj|$path"
62    if {[info exists _xp2obj($handle)]} {
63        set obj $_xp2obj($handle)
64        incr _obj2ref($obj)
65        return $obj
66    }
67
68    set obj [Rappture::Mesh ::#auto $xmlobj $path]
69    set _xp2obj($handle) $obj
70    set _obj2ref($obj) 1
71    return $obj
72}
73
74# ----------------------------------------------------------------------
75# USAGE: Rappture::Mesh::release <obj>
76#
77# Clients call this when they're no longer using a Mesh fetched
78# previously by the "fetch" proc.  This decrements the reference
79# count for the mesh and destroys the object when it is no longer
80# in use.
81# ----------------------------------------------------------------------
82itcl::body Rappture::Mesh::release {obj} {
83    if {[info exists _obj2ref($obj)]} {
84        incr _obj2ref($obj) -1
85        if {$_obj2ref($obj) <= 0} {
86            unset _obj2ref($obj)
87            foreach handle [array names _xp2obj] {
88                if {$_xp2obj($handle) == $obj} {
89                    unset _xp2obj($handle)
90                }
91            }
92            itcl::delete object $obj
93        }
94    } else {
95        error "can't find reference count for $obj"
96    }
97}
98
99# ----------------------------------------------------------------------
100# CONSTRUCTOR
101# ----------------------------------------------------------------------
102itcl::body Rappture::Mesh::constructor {xmlobj path} {
103    package require vtk
104    if {![Rappture::library isvalid $xmlobj]} {
105        error "bad value \"$xmlobj\": should be Rappture::library"
106    }
107    set _xmlobj $xmlobj
108    set _mesh [$xmlobj element -as object $path]
109
110    set u [$_mesh get units]
111    if {"" != $u} {
112        while {[llength $u] < 3} {
113            lappend u [lindex $u end]
114        }
115        set _units $u
116    }
117
118    foreach lim {xmin xmax ymin ymax zmin zmax} {
119        set _limits($lim) ""
120    }
121
122    if {[$_mesh element vtk] != ""} {
123        _buildRectMesh $xmlobj $path
124    } elseif {[$_mesh element node] != "" && [$_mesh element element] != ""} {
125        _buildNodesElements $xmlobj $path
126    } else {
127        error "can't find mesh data in $path"
128    }
129}
130
131# ----------------------------------------------------------------------
132# DESTRUCTOR
133# ----------------------------------------------------------------------
134itcl::body Rappture::Mesh::destructor {} {
135    # don't destroy the _xmlobj! we don't own it!
136    itcl::delete object $_mesh
137
138    catch {rename $this-points ""}
139    catch {rename $this-grid ""}
140    catch {rename $this-dset ""}
141
142    foreach type [array names _pts2elem] {
143        rename $_pts2elem($type) ""
144    }
145}
146
147# ----------------------------------------------------------------------
148# USAGE: points
149#
150# Returns the vtk object containing the points for this mesh.
151# ----------------------------------------------------------------------
152itcl::body Rappture::Mesh::points {} {
153    return $_pdata
154}
155
156# ----------------------------------------------------------------------
157# USAGE: elements
158#
159# Returns a list of the form {plist r plist r ...} for each element
160# in this mesh.  Each plist is a list of {x y x y ...} coordinates
161# for the mesh.
162# ----------------------------------------------------------------------
163itcl::body Rappture::Mesh::elements {} {
164    # build a map for region number => region type
165    foreach comp [$_mesh children -type region] {
166        set id [$_mesh element -as id $comp]
167        set regions($id) [$_mesh get $comp]
168    }
169    set regions() "unknown"
170
171    set rlist ""
172    foreach comp [$_mesh children -type element] {
173        set rid [$_mesh get $comp.region]
174
175        #
176        # HACK ALERT!
177        #
178        # Prophet puts out nodes in a funny "Z" shaped order,
179        # not in proper clockwise fashion.  Switch the last
180        # two nodes for now to make them correct.
181        #
182        set nlist [$_mesh get $comp.nodes]
183        set n2 [lindex $nlist 2]
184        set n3 [lindex $nlist 3]
185        set nlist [lreplace $nlist 2 3 $n3 $n2]
186        lappend nlist [lindex $nlist 0]
187
188        set plist ""
189        foreach nid $nlist {
190            eval lappend plist [$_mesh get node($nid)]
191        }
192        lappend rlist $plist $regions($rid)
193    }
194    return $rlist
195}
196
197# ----------------------------------------------------------------------
198# USAGE: mesh
199#
200# Returns the vtk object representing the mesh.
201# ----------------------------------------------------------------------
202itcl::body Rappture::Mesh::mesh {} {
203    return $_gdata
204}
205
206# ----------------------------------------------------------------------
207# USAGE: size ?-points|-elements?
208#
209# Returns the number of points in this mesh.
210# ----------------------------------------------------------------------
211itcl::body Rappture::Mesh::size {{what -points}} {
212    switch -- $what {
213        -points {
214            return [$_pdata GetNumberOfPoints]
215        }
216        -elements {
217            return [$_gdata GetNumberOfCells]
218        }
219        default {
220            error "bad option \"$what\": should be -points or -elements"
221        }
222    }
223}
224
225# ----------------------------------------------------------------------
226# USAGE: dimensions
227#
228# Returns the number of dimensions for this object: 1, 2, or 3.
229# ----------------------------------------------------------------------
230itcl::body Rappture::Mesh::dimensions {} {
231    # count the dimensions with real limits
232    set dims 0
233    foreach d {x y z} {
234        if {$_limits(${d}min) != $_limits(${d}max)} {
235            incr dims
236        }
237    }
238    return $dims
239}
240
241# ----------------------------------------------------------------------
242# USAGE: limits x|y|z
243#
244# Returns the {min max} values for the limits of the specified axis.
245# ----------------------------------------------------------------------
246itcl::body Rappture::Mesh::limits {which} {
247    if {![info exists _limits(${which}min)]} {
248        error "bad axis \"$which\": should be x, y, z"
249    }
250    return [list $_limits(${which}min) $_limits(${which}max)]
251}
252
253# ----------------------------------------------------------------------
254# USAGE: hints ?<keyword>?
255#
256# Returns a list of key/value pairs for various hints about plotting
257# this field.  If a particular <keyword> is specified, then it returns
258# the hint for that <keyword>, if it exists.
259# ----------------------------------------------------------------------
260itcl::body Rappture::Mesh::hints {{keyword ""}} {
261    foreach key {label color units} {
262        set str [$_mesh get $key]
263        if {"" != $str} {
264            set hints($key) $str
265        }
266    }
267
268    if {$keyword != ""} {
269        if {[info exists hints($keyword)]} {
270            return $hints($keyword)
271        }
272        return ""
273    }
274    return [array get hints]
275}
276
277# ----------------------------------------------------------------------
278# USAGE: _buildNodesElements <xmlobj> <path>
279#
280# Used internally to build a mesh representation based on nodes and
281# elements stored in the XML.
282# ----------------------------------------------------------------------
283itcl::body Rappture::Mesh::_buildNodesElements {xmlobj path} {
284    # create the vtk objects containing points and connectivity
285    vtkPoints $this-points
286    set _pdata $this-points
287    vtkUnstructuredGrid $this-grid
288    set _gdata $this-grid
289
290    #
291    # Extract each node and add it to the points list.
292    #
293    foreach comp [$xmlobj children -type node $path] {
294        set xyz [$xmlobj get $path.$comp]
295
296        # make sure we have x,y,z
297        while {[llength $xyz] < 3} {
298            lappend xyz "0"
299        }
300
301        # extract each point and add it to the points list
302        foreach {x y z} $xyz break
303        foreach dim {x y z} units $_units {
304            set v [Rappture::Units::convert [set $dim] \
305                -context $units -to $units -units off]
306
307            set $dim $v  ;# save back to real x/y/z variable
308
309            if {"" == $_limits(${dim}min)} {
310                set _limits(${dim}min) $v
311                set _limits(${dim}max) $v
312            } else {
313                if {$v < $_limits(${dim}min)} { set _limits(${dim}min) $v }
314                if {$v > $_limits(${dim}max)} { set _limits(${dim}max) $v }
315            }
316        }
317        $this-points InsertNextPoint $x $y $z
318    }
319    $this-grid SetPoints $this-points
320
321    #
322    # Extract each element and add it to the mesh.
323    #
324    foreach comp [$xmlobj children -type element $path] {
325        set nlist [$_mesh get $comp.nodes]
326        set elem [_getVtkElement [llength $nlist]]
327
328        set i 0
329        foreach n $nlist {
330            [$elem GetPointIds] SetId $i $n
331            incr i
332        }
333        $this-grid InsertNextCell [$elem GetCellType] [$elem GetPointIds]
334    }
335}
336
337# ----------------------------------------------------------------------
338# USAGE: _buildRectMesh <xmlobj> <path>
339#
340# Used internally to build a mesh representation based on a native
341# VTK file for a rectangular mesh.
342# ----------------------------------------------------------------------
343itcl::body Rappture::Mesh::_buildRectMesh {xmlobj path} {
344    vtkRectilinearGridReader $this-gr
345    $this-gr ReadFromInputStringOn
346    $this-gr SetInputString [$xmlobj get $path.vtk]
347
348    set _gdata [$this-gr GetOutput]
349    set _pdata [$_gdata GetPointData]
350
351    $_gdata Update
352    foreach name {xmin xmax ymin ymax zmin zmax} val [$_gdata GetBounds] {
353        set _limits($name) $val
354    }
355}
356
357# ----------------------------------------------------------------------
358# USAGE: _getVtkElement <npts>
359#
360# Used internally to find (or allocate, if necessary) a vtk element
361# that can be used to build up a mesh.  The element depends on the
362# number of points passed in.  4 points is a tetrahedron, 5 points
363# is a pyramid, etc.
364# ----------------------------------------------------------------------
365itcl::body Rappture::Mesh::_getVtkElement {npts} {
366    if {![info exists _pts2elem($npts)]} {
367        switch -- $npts {
368            4 {
369                set _pts2elem($npts) $this-elem4
370                vtkTetra $_pts2elem($npts)
371            }
372            5 {
373                set _pts2elem($npts) $this-elem5
374                vtkPyramid $_pts2elem($npts)
375            }
376            6 {
377                set _pts2elem($npts) $this-elem6
378                vtkWedge $_pts2elem($npts)
379            }
380            8 {
381                set _pts2elem($npts) $this-elem8
382                vtkVoxel $_pts2elem($npts)
383            }
384        }
385    }
386    return $_pts2elem($npts)
387}
Note: See TracBrowser for help on using the repository browser.