[4897] | 1 | from Numeric import Float, zeros, shape |
---|
[3873] | 2 | from Tkinter import Button, E, Tk, W |
---|
[3540] | 3 | from threading import Event |
---|
[3465] | 4 | from visualiser import Visualiser |
---|
[3625] | 5 | from vtk import vtkCellArray, vtkPoints, vtkPolyData |
---|
[3465] | 6 | |
---|
| 7 | class RealtimeVisualiser(Visualiser): |
---|
| 8 | """A VTK-powered realtime visualiser which runs in its own thread. |
---|
[3873] | 9 | In addition to the functions provided by the standard visualiser, |
---|
| 10 | the following additional functions are provided: |
---|
| 11 | |
---|
| 12 | update() - Sync the visualiser to the current state of the model. |
---|
| 13 | Should be called inside the evolve loop. |
---|
| 14 | |
---|
| 15 | evolveFinished() - Clean up synchronisation constructs that tie the |
---|
| 16 | visualiser to the evolve loop. Call this after the evolve loop finishes |
---|
| 17 | to ensure a clean shutdown. |
---|
[3465] | 18 | """ |
---|
[3493] | 19 | def __init__(self, source): |
---|
| 20 | """The source parameter is assumed to be a Domain. |
---|
| 21 | """ |
---|
| 22 | Visualiser.__init__(self, source) |
---|
[3465] | 23 | |
---|
[3493] | 24 | self.running = True |
---|
| 25 | |
---|
[3873] | 26 | self.xmin = None |
---|
| 27 | self.xmax = None |
---|
| 28 | self.ymin = None |
---|
| 29 | self.ymax = None |
---|
| 30 | self.zmin = None |
---|
| 31 | self.zmax = None |
---|
| 32 | |
---|
[3493] | 33 | # Synchronisation Constructs |
---|
| 34 | self.sync_idle = Event() |
---|
| 35 | self.sync_idle.clear() |
---|
| 36 | self.sync_unpaused = Event() |
---|
| 37 | self.sync_unpaused.set() |
---|
| 38 | self.sync_redrawReady = Event() |
---|
| 39 | self.sync_redrawReady.clear() |
---|
[3966] | 40 | |
---|
[3625] | 41 | def setup_grid(self): |
---|
| 42 | self.vtk_cells = vtkCellArray() |
---|
[3960] | 43 | triangles = self.source.get_triangles() |
---|
| 44 | N_tri = len(self.source) |
---|
[3958] | 45 | verticies = self.source.get_vertex_coordinates() |
---|
| 46 | N_vert = len(verticies) |
---|
[4897] | 47 | |
---|
[3625] | 48 | # Also build vert_index - a list of the x & y values of each vertex |
---|
| 49 | self.vert_index = zeros((N_vert,2), Float) |
---|
[3958] | 50 | for n in range(N_tri): |
---|
[3625] | 51 | self.vtk_cells.InsertNextCell(3) |
---|
| 52 | for v in range(3): |
---|
[4897] | 53 | self.vert_index[n * 3 + v] = verticies[n * 3 + v] |
---|
| 54 | self.vtk_cells.InsertCellPoint(n * 3 + v) |
---|
[3625] | 55 | |
---|
| 56 | def update_height_quantity(self, quantityName, dynamic=True): |
---|
[3960] | 57 | N_vert = len(self.source.get_vertex_coordinates()) |
---|
[3625] | 58 | qty_index = zeros(N_vert, Float) |
---|
[3960] | 59 | triangles = self.source.get_triangles() |
---|
[3963] | 60 | vertex_values, _ = self.source.get_quantity(quantityName).get_vertex_values(xy=False, smooth=False) |
---|
[3625] | 61 | |
---|
[4897] | 62 | for n in range(N_vert): |
---|
| 63 | qty_index[n] = vertex_values[n] |
---|
[3625] | 64 | |
---|
| 65 | points = vtkPoints() |
---|
| 66 | for v in range(N_vert): |
---|
| 67 | points.InsertNextPoint(self.vert_index[v][0], |
---|
| 68 | self.vert_index[v][1], |
---|
| 69 | qty_index[v] * self.height_zScales[quantityName] |
---|
| 70 | + self.height_offset[quantityName]) |
---|
[3873] | 71 | if self.xmin == None or self.xmin > self.vert_index[v][0]: |
---|
| 72 | self.xmin = self.vert_index[v][0] |
---|
| 73 | if self.xmax == None or self.xmax < self.vert_index[v][0]: |
---|
| 74 | self.xmax = self.vert_index[v][0] |
---|
| 75 | if self.ymin == None or self.ymin > self.vert_index[v][1]: |
---|
| 76 | self.ymin = self.vert_index[v][1] |
---|
| 77 | if self.ymax == None or self.ymax < self.vert_index[v][1]: |
---|
| 78 | self.ymax = self.vert_index[v][1] |
---|
| 79 | if self.zmin == None or self.zmin > qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName]: |
---|
| 80 | self.zmin = qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName] |
---|
| 81 | if self.zmax == None or self.zmax < qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName]: |
---|
| 82 | self.zmax = qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName] |
---|
[3625] | 83 | |
---|
| 84 | polydata = self.vtk_polyData[quantityName] = vtkPolyData() |
---|
| 85 | polydata.SetPoints(points) |
---|
| 86 | polydata.SetPolys(self.vtk_cells) |
---|
[3873] | 87 | |
---|
| 88 | def get_3d_bounds(self): |
---|
| 89 | return [self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax] |
---|
[3625] | 90 | |
---|
| 91 | def build_quantity_dict(self): |
---|
[3960] | 92 | triangles = self.source.get_triangles() |
---|
[3625] | 93 | quantities = {} |
---|
[3963] | 94 | for q in self.source.get_quantity_names(): |
---|
[3960] | 95 | quantities[q], _ = self.source.get_quantity(q).get_vertex_values(xy=False) |
---|
[3625] | 96 | return quantities |
---|
| 97 | |
---|
[3465] | 98 | def setup_gui(self): |
---|
| 99 | Visualiser.setup_gui(self) |
---|
[3549] | 100 | self.tk_pauseResume = Button(self.tk_controlFrame, text="Pause", command=self.pauseResume) |
---|
[3540] | 101 | self.tk_pauseResume.grid(row=1, column=0, sticky=E+W) |
---|
[3465] | 102 | |
---|
| 103 | def pauseResume(self): |
---|
| 104 | if self.sync_unpaused.isSet(): |
---|
| 105 | self.sync_unpaused.clear() |
---|
| 106 | self.tk_pauseResume.config(text="Resume") |
---|
| 107 | else: |
---|
| 108 | self.sync_unpaused.set() |
---|
| 109 | self.tk_pauseResume.config(text="Pause") |
---|
[3493] | 110 | |
---|
| 111 | def shutdown(self): |
---|
| 112 | Visualiser.shutdown(self) |
---|
| 113 | self.running = False |
---|
| 114 | self.sync_idle.set() |
---|
| 115 | self.sync_unpaused.set() |
---|
[3625] | 116 | |
---|
[3873] | 117 | def redraw(self): |
---|
[3918] | 118 | if self.running and self.sync_unpaused.isSet(): |
---|
[3873] | 119 | self.sync_redrawReady.wait() |
---|
| 120 | self.sync_redrawReady.clear() |
---|
| 121 | self.redraw_quantities() |
---|
| 122 | self.sync_idle.set() |
---|
| 123 | Visualiser.redraw(self) |
---|
| 124 | |
---|
[4897] | 125 | def update(self,pause=False): |
---|
[3873] | 126 | """Sync the visualiser to the domain. Call this in the evolve loop.""" |
---|
[4897] | 127 | |
---|
[3873] | 128 | if self.running: |
---|
| 129 | self.sync_redrawReady.set() |
---|
| 130 | self.sync_idle.wait() |
---|
| 131 | self.sync_idle.clear() |
---|
| 132 | self.sync_unpaused.wait() |
---|
| 133 | |
---|
[4897] | 134 | if pause and self.running: |
---|
| 135 | if self.sync_unpaused.isSet(): |
---|
| 136 | self.sync_unpaused.clear() |
---|
| 137 | self.tk_pauseResume.config(text="Resume") |
---|
| 138 | |
---|
| 139 | self.sync_redrawReady.set() |
---|
| 140 | self.sync_idle.wait() |
---|
| 141 | self.sync_idle.clear() |
---|
| 142 | self.sync_unpaused.wait() |
---|
| 143 | |
---|
[6113] | 144 | return self.running |
---|
[4897] | 145 | |
---|
[3873] | 146 | def evolveFinished(self): |
---|
| 147 | """Stop the visualiser from waiting on signals from the evolve loop. |
---|
| 148 | Call this just after the evolve loop to ensure a clean shutdown.""" |
---|
| 149 | self.running = False |
---|
| 150 | self.sync_redrawReady.set() |
---|