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

Last change on this file since 1313 was 136, checked in by mmc, 19 years ago
  • Fixed field/mesh to support rectilinear meshes in "vtk native" format. This makes it much faster to load large meshes. Needed for app-qdot.
  • Fixed contour viewer to show separate x/y/z cut planes and isosurfaces.
  • Fixed energy level viewer to label "HOMO" as "Ground State", if that is the language used by the table.
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
17package require vtk
18
19namespace eval Rappture { # forward declaration }
20
21itcl::class Rappture::Mesh {
22    constructor {xmlobj path} { # defined below }
23    destructor { # defined below }
24
25    public method points {}
26    public method elements {}
27    public method mesh {}
28    public method size {{what -points}}
29    public method dimensions {}
30    public method limits {which}
31    public method hints {{key ""}}
32
33    public proc fetch {xmlobj path}
34    public proc release {obj}
35
36    private method _buildNodesElements {xmlobj path}
37    private method _buildRectMesh {xmlobj path}
38    private method _getVtkElement {npts}
39
40    private variable _xmlobj ""  ;# ref to XML obj with device data
41    private variable _mesh ""    ;# lib obj representing this mesh
42    private variable _pts2elem   ;# maps # points => vtk element
43
44    private variable _units "m m m" ;# system of units for x, y, z
45    private variable _limits        ;# limits xmin, xmax, ymin, ymax, ...
46    private variable _pdata ""      ;# name of vtkPointData object
47    private variable _gdata ""      ;# name of vtkDataSet object
48
49    private common _xp2obj       ;# used for fetch/release ref counting
50    private common _obj2ref      ;# used for fetch/release ref counting
51}
52
53# ----------------------------------------------------------------------
54# USAGE: Rappture::Mesh::fetch <xmlobj> <path>
55#
56# Clients use this instead of a constructor to fetch the Mesh for
57# a particular <path> in the <xmlobj>.  When the client is done with
58# the mesh, he calls "release" to decrement the reference count.
59# When the mesh is no longer needed, it is cleaned up automatically.
60# ----------------------------------------------------------------------
61itcl::body Rappture::Mesh::fetch {xmlobj path} {
62    set handle "$xmlobj|$path"
63    if {[info exists _xp2obj($handle)]} {
64        set obj $_xp2obj($handle)
65        incr _obj2ref($obj)
66        return $obj
67    }
68
69    set obj [Rappture::Mesh ::#auto $xmlobj $path]
70    set _xp2obj($handle) $obj
71    set _obj2ref($obj) 1
72    return $obj
73}
74
75# ----------------------------------------------------------------------
76# USAGE: Rappture::Mesh::release <obj>
77#
78# Clients call this when they're no longer using a Mesh fetched
79# previously by the "fetch" proc.  This decrements the reference
80# count for the mesh and destroys the object when it is no longer
81# in use.
82# ----------------------------------------------------------------------
83itcl::body Rappture::Mesh::release {obj} {
84    if {[info exists _obj2ref($obj)]} {
85        incr _obj2ref($obj) -1
86        if {$_obj2ref($obj) <= 0} {
87            unset _obj2ref($obj)
88            foreach handle [array names _xp2obj] {
89                if {$_xp2obj($handle) == $obj} {
90                    unset _xp2obj($handle)
91                }
92            }
93            itcl::delete object $obj
94        }
95    } else {
96        error "can't find reference count for $obj"
97    }
98}
99
100# ----------------------------------------------------------------------
101# CONSTRUCTOR
102# ----------------------------------------------------------------------
103itcl::body Rappture::Mesh::constructor {xmlobj path} {
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.