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

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

Fixed the visualiser to actually run properly in its own thread.

Added a simple example to use the realtime visualiser.

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