source: anuga_core/source/anuga/visualiser/offline.py @ 3670

Last change on this file since 3670 was 3670, checked in by jack, 18 years ago

Added axes support to the visualiser.

File size: 7.6 KB
Line 
1from Numeric import array, Float, ravel, zeros
2from Scientific.IO.NetCDF import NetCDFFile
3from Tkinter import Button, E, W
4from visualiser import Visualiser
5from vtk import vtkCellArray, vtkPoints, vtkPolyData
6
7class OfflineVisualiser(Visualiser):
8    """A VTK-powered offline visualiser which runs in its own thread.
9    In addition to the functions provided by the standard visualiser,
10    the following additional functions are provided:
11
12    precache_height_quantities() - Precache all the vtkpoints
13    structures for any dynamic height based quantities to render.
14    """
15    def __init__(self, source):
16        """The source parameter is assumed to be a NetCDF sww file.
17        """
18        Visualiser.__init__(self, source)
19
20        self.frameNumber = 0
21        fin = NetCDFFile(self.source, 'r')
22        self.maxFrameNumber = fin.variables['time'].shape[0] - 1
23        fin.close()
24
25        self.xmin = None
26        self.xmax = None
27        self.ymin = None
28        self.ymax = None
29        self.zmin = None
30        self.zmax = None
31
32        self.vtk_heightQuantityCache = []
33        for i in range(self.maxFrameNumber + 1): # maxFrameNumber is zero indexed.
34            self.vtk_heightQuantityCache.append({})
35
36        self.paused = False
37       
38    def setup_grid(self):
39        fin = NetCDFFile(self.source, 'r')
40        self.vtk_cells = vtkCellArray()
41        N_tri = fin.variables['volumes'].shape[0]
42        for v in range(N_tri):
43            self.vtk_cells.InsertNextCell(3)
44            for i in range(3):
45                self.vtk_cells.InsertCellPoint(fin.variables['volumes'][v][i])
46        fin.close()
47       
48    def update_height_quantity(self, quantityName, dynamic=True):
49        polydata = self.vtk_polyData[quantityName] = vtkPolyData()
50        if dynamic is True:
51            if not self.vtk_heightQuantityCache[self.frameNumber].has_key(quantityName):
52                self.vtk_heightQuantityCache[self.frameNumber][quantityName]\
53                    = self.read_height_quantity(quantityName, True, self.frameNumber);
54            polydata.SetPoints(self.vtk_heightQuantityCache[self.frameNumber][quantityName])
55        else:
56            polydata.SetPoints(self.read_height_quantity(quantityName, False))
57        polydata.SetPolys(self.vtk_cells)
58
59    def get_3d_bounds(self):
60        return [self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax]
61           
62    def read_height_quantity(self, quantityName, dynamic=True, frameNumber=0):
63        """Read in a height based quantity from the NetCDF source file
64        and return a vtkPoints object. frameNumber is ignored if
65        dynamic is false."""
66        fin = NetCDFFile(self.source, 'r')
67        points = vtkPoints()
68        if dynamic is True:
69            N_vert = fin.variables[quantityName].shape[1]
70        else:
71            N_vert = len(fin.variables[quantityName])
72        x = ravel(array(fin.variables['x'], Float))
73        y = ravel(array(fin.variables['y'], Float))
74        if dynamic is True:
75            q = array(fin.variables[quantityName][frameNumber], Float)
76        else:
77            q = ravel(array(fin.variables[quantityName], Float))
78
79        q *= self.height_zScales[quantityName]
80        q += self.height_offset[quantityName]
81
82        for v in range(N_vert):
83            points.InsertNextPoint(x[v], y[v], q[v])
84            if self.xmin == None or self.xmin > x[v]:
85                self.xmin = x[v]
86            if self.xmax == None or self.xmax < x[v]:
87                self.xmax = x[v]
88            if self.ymin == None or self.ymin > y[v]:
89                self.ymin = y[v]
90            if self.ymax == None or self.ymax < y[v]:
91                self.ymax = y[v]
92            if self.zmin == None or self.zmin > q[v]:
93                self.zmin = q[v]
94            if self.zmax == None or self.zmax < q[v]:
95                self.zmax = q[v]
96        fin.close()
97        return points
98
99    def precache_height_quantities(self):
100        """Precache any height-based quantities. Call before rendering
101        beigns."""
102        for q in self.height_quantities:
103            if self.height_dynamic[q] is True:
104                print 'Precaching %s' % q
105                for i in range(self.maxFrameNumber + 1): # maxFrameNumber is zero-indexed
106                    print ' - Frame %d of %d' % (i, self.maxFrameNumber)
107                    self.vtk_heightQuantityCache[i][q]\
108                        = self.read_height_quantity(q, True, i)
109
110    def build_quantity_dict(self):
111        quantities = {}
112        fin = NetCDFFile(self.source, 'r')
113        for q in filter(lambda n:n != 'x' and n != 'y' and n != 'z' and n != 'time' and n != 'volumes', fin.variables.keys()):
114            if len(fin.variables[q].shape) == 1: # Not a time-varying quantity
115                quantities[q] = ravel(array(fin.variables[q], Float))
116            else: # Time-varying, get the current timestep data
117                quantities[q] = array(fin.variables[q][self.frameNumber], Float)
118        fin.close()
119        return quantities
120
121    def setup_gui(self):
122        Visualiser.setup_gui(self)
123        self.tk_quit.grid(row=0, column=0, columnspan=6, sticky=W+E)
124        self.tk_restart = Button(self.tk_controlFrame, text="<<<", command=self.restart)
125        self.tk_restart.grid(row=1, column=0, sticky=W+E)
126        self.tk_back10 = Button(self.tk_controlFrame, text="<<", command=self.back10)
127        self.tk_back10.grid(row=1, column=1, sticky=W+E)
128        self.tk_back = Button(self.tk_controlFrame, text="<", command=self.back)
129        self.tk_back.grid(row=1, column=2, sticky=W+E)
130        self.tk_pauseResume = Button(self.tk_controlFrame, text="Pause", command=self.pauseResume, width=15)
131        self.tk_pauseResume.grid(row=1, column=3, sticky=W+E)
132        self.tk_forward = Button(self.tk_controlFrame, text=">", command=self.forward)
133        self.tk_forward.grid(row=1, column=4, sticky=W+E)
134        self.tk_forward10 = Button(self.tk_controlFrame, text=">>", command=self.forward10)
135        self.tk_forward10.grid(row=1, column=5, sticky=W+E)
136
137        # Make the buttons stretch to fill all available space
138        for i in range(6):
139            self.tk_controlFrame.grid_columnconfigure(i, weight=1)
140
141    def run(self):
142        self.tk_root.after(100, self.animateForward)
143        Visualiser.run(self)
144
145    def restart(self):
146        self.frameNumber = 0
147        self.redraw_quantities(True)
148        self.pause()
149
150    def back10(self):
151        if self.frameNumber - 10 >= 0:
152            self.frameNumber -= 10
153        else:
154            self.frameNumber = 0
155        self.redraw_quantities(True)
156        self.pause()
157
158    def back(self):
159        if self.frameNumber > 0:
160            self.frameNumber -= 1
161            self.redraw_quantities(True)
162            self.pause()
163
164    def pauseResume(self):
165        if self.paused is True:
166            self.resume()
167        else:
168            self.pause()
169
170    def pause(self):
171        self.paused = True
172        self.tk_pauseResume.config(text="Resume")
173
174    def resume(self):
175        self.paused = False
176        self.tk_pauseResume.config(text="Pause")
177        self.tk_root.after(100, self.animateForward)
178
179    def forward(self):
180        self.forward_step()
181        self.pause()
182
183    def forward_step(self):
184        if self.frameNumber < self.maxFrameNumber:
185            self.frameNumber += 1
186            self.redraw_quantities(True)
187        else:
188            self.pause()
189
190    def forward10(self):
191        if self.frameNumber + 10 <= self.maxFrameNumber:
192            self.frameNumber += 10
193        else:
194            self.frameNumber = self.maxFrameNumber
195        self.redraw_quantities(True)
196        self.pause()
197
198    def animateForward(self):
199        if self.paused is not True:
200            self.forward_step()
201            self.tk_root.after(100, self.animateForward)
Note: See TracBrowser for help on using the repository browser.