source: inundation/pyvolution/vtk_realtime_visualiser.py @ 2267

Last change on this file since 2267 was 2267, checked in by jack, 19 years ago

Added pause and resume buttons to the vtk visualiser. This
will freeze the evolve thread, allowing interaction with the visualiser.

Disabled use of the vtk_visualiser_ext module, because it doesn't link on windows.
The prebuilt vtk DLLs are built with VC++ and the rest of the project is being built with
MinGW, which don't link together. The speed increase wasn't noticable anyway.

File size: 8.2 KB
Line 
1# VTK-Based visualiser
2# intended to replace realtime_visualisation_new.py
3
4import threading
5import Tkinter
6import vtk
7from Numeric import *
8from vtk.tk.vtkTkRenderWidget import vtkTkRenderWidget
9from vtk_visualiser_ext import make_vtkpoints
10
11class Visualiser(threading.Thread):
12
13    """A VTK-powered visualiser, designed to replace the VPython one.
14    Intended to be run in its own thread.
15
16    Customisation options for the visualiser are as follows:
17
18    setup: Dictionary mapping quantity name -> boolean.
19    if setup[q] is true, draw the quantity when the visualiser starts.
20
21    updating: Dictionary mapping quantity name -> boolean.
22    if updating[q] is true, update the rendering of this quantity each
23    timestep.
24
25    qcolor: Dictionary mapping quantity name -> (float, float, float)
26    if the name of a quantity is found in qcolor, the colour specified (in
27    (r, g, b) from 0.0 to 1.0) is used for display of that quantity instead of
28    (0.5, 0.5, 0.5) (the default)
29
30    scale_z: Dictionary mapping quantity name -> float
31    Override the z scaling of this quantity with the float specified.
32
33    default_scale_z: float
34    multiply all z coordinates by this amount unless an overriding value
35    exists in scale_z.
36    """
37
38    def __init__(self, domain, default_scale_z=1.0, rect=None, title='Test'):
39        threading.Thread.__init__(self)
40        # Initialise data structures. setup and updating are maps
41        # quantity name -> boolean, setup means to render it,
42        # update means to update with time.
43        # qcolor maps quantity name -> (float, float, float): the colour (r, g, b)
44        self.setup = {}
45        self.updating = {}
46        self.qcolor = {}
47        self.scale_z = {}
48        self.default_scale_z = default_scale_z
49        self.domain = domain
50        self.vertices = domain.vertex_coordinates
51
52        self.idle = threading.Event()
53        self.redraw_ready = threading.Event()
54        self.unpaused = threading.Event()
55        self.unpaused.set()
56
57        # Internal use - storage of vtk objects
58        self.grids = {}
59        self.actors = {}
60        self.polydata = {}
61        self.mappers = {}
62
63        # Default options
64        for x in self.domain.quantities:
65            self.setup[x] = False
66            self.updating[x] = False
67
68        # Bounding box.
69        if rect is None:
70            self.max_x = max(max(self.vertices[:,0]),max(self.vertices[:,2]),max(self.vertices[:,4]))
71            self.min_x = min(min(self.vertices[:,0]),min(self.vertices[:,2]),min(self.vertices[:,4]))
72            self.max_y = max(max(self.vertices[:,1]),max(self.vertices[:,3]),max(self.vertices[:,5]))
73            self.min_y = min(min(self.vertices[:,1]),min(self.vertices[:,3]),min(self.vertices[:,5]))
74        else:
75            self.max_x = rect[2]
76            self.min_x = rect[0]
77            self.max_y = rect[3]
78            self.min_y = rect[1]
79
80        self.range_x = self.max_x - self.min_x
81        self.range_y = self.max_y - self.min_y
82        self.range_xy = max(self.range_x, self.range_y)
83
84    def run(self):
85        self.initialise_gui()
86        self.add_axes()
87        self.setup_all()
88        self.root.after(100, self.idle.set)
89        self.root.mainloop()
90
91    def initialise_gui(self):
92        """Prepare the GUI for the Visualiser, and set up the
93        renderer.
94
95        """
96        # Using the TK VTK widget allows extendability
97        # should a gui need adding.
98        self.root = Tkinter.Tk()
99
100        # Message handling with after
101        self.root.after(100, self.redraw)
102
103        self.renderWidget = vtkTkRenderWidget(self.root, width=400, height=400)
104        self.renderWidget.pack(expand='true', fill='both')
105        self.renderWindow = self.renderWidget.GetRenderWindow()
106        self.renderer = vtk.vtkRenderer()
107        self.renderWindow.AddRenderer(self.renderer)
108
109        self.quitButton = Tkinter.Button(self.root, text='Quit', command=self.shutdown)
110        self.quitButton.pack(side=Tkinter.RIGHT)
111
112        self.pauseButton = Tkinter.Button(self.root, text='Pause', command=self.unpaused.clear)
113        self.pauseButton.pack(side=Tkinter.LEFT)
114
115        self.resumeButton = Tkinter.Button(self.root, text='Resume', command=self.unpaused.set)
116        self.resumeButton.pack(side=Tkinter.LEFT)
117
118    def add_axes(self):
119        """Add axes to this Visualiser
120        """
121        pass
122
123    def setup_all(self):
124        """Draw in the data that is specified by setup or update
125        """
126
127        self.N_tri = len(self.domain.triangles)
128        self.N_vert = len(self.vertices)
129        self.cells = vtk.vtkCellArray()
130        self.vertices = self.domain.get_vertex_coordinates()
131        self.vert_index = zeros((self.N_vert,2), Float)
132        for n in range(self.N_vert):
133            for i in range(3):
134                self.vert_index[self.domain.triangles[n][i]] = self.vertices[n][i*2:i*2+2]
135
136        # Prepare the list of cells
137        for t in self.domain.triangles:
138            self.cells.InsertNextCell(3)
139            for i in range(3):
140                self.cells.InsertCellPoint(t[i])
141
142        # Set up the rendering of each quantity
143        for q in self.domain.quantities:
144            if self.setup[q] | self.updating[q]:
145                self.draw_quantity(q)
146
147    def draw_quantity(self, q):
148        if self.scale_z.has_key(q):
149            scale = self.scale_z[q]
150        else:
151            scale = self.default_scale_z
152        #############################################################
153        # Disabled the make_vtkpoints call because the extension is
154        # difficult to get to link under windows
155        #############################################################
156        #self.grids[q] = make_vtkpoints(self.N_tri,
157        #                           self.N_vert,
158        #                           scale,
159        #                           self.domain.quantities[q].vertex_values,
160        #                           self.vert_index,
161        #                           self.domain.triangles)
162        #grid = self.grids[q]
163        #############################################################
164       
165        qty_index = zeros(self.N_vert, Float)
166        for n in range(self.N_tri):
167            for v in range(3):
168                qty_index[self.domain.triangles[n][v]] = self.domain.quantities[q].vertex_values[n][v]
169               
170        self.grids[q] = vtk.vtkPoints()
171        grid = self.grids[q]
172       
173        for v in range(self.N_vert):
174            grid.InsertNextPoint(self.vert_index[v][0],
175                                 self.vert_index[v][1],
176                                 qty_index[v] * scale)
177
178        # Can't recycle vtkPolyData objects: Apparently they behave
179        # unusually if the points (i.e. vertex data) is set after
180        # the polys (i.e. triangle data)
181        self.polydata[q] = vtk.vtkPolyData()
182        polydata = self.polydata[q]
183
184        polydata.SetPoints(grid)
185        polydata.SetPolys(self.cells)
186
187        if self.mappers.has_key(q):
188            mapper = self.mappers[q]
189            mapper.SetInput(polydata)
190            mapper.Update()
191        else:
192            self.mappers[q] = vtk.vtkPolyDataMapper()
193            mapper = self.mappers[q]
194            mapper.SetInput(polydata)
195
196        if not self.actors.has_key(q):
197            self.actors[q] = vtk.vtkActor()
198            actor = self.actors[q]
199            actor.SetMapper(mapper)
200
201            if self.qcolor.has_key(q):
202                actor.GetProperty().SetColor(self.qcolor[q])
203            else:
204                actor.GetProperty().SetColor(0.5, 0.5, 0.5)
205
206            self.renderer.AddActor(actor)
207
208    def redraw(self):
209        if self.redraw_ready.isSet():
210            self.redraw_ready.wait()
211            self.redraw_ready.clear()
212            for q in self.domain.quantities:
213                if self.updating[q]:
214                    self.draw_quantity(q)
215
216            self.renderWindow.Render()
217            self.root.update_idletasks()
218            self.idle.set()
219        self.root.after(100, self.redraw)
220
221    def shutdown(self):
222        self.domain.visualise = False
223        self.idle.set()
224        self.unpaused.set()
225        self.root.withdraw()
226        self.root.quit()
Note: See TracBrowser for help on using the repository browser.