[3625] | 1 | from Numeric import Float, zeros |
---|
[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) |
---|
[3625] | 47 | # Also build vert_index - a list of the x & y values of each vertex |
---|
| 48 | self.vert_index = zeros((N_vert,2), Float) |
---|
[3958] | 49 | for n in range(N_tri): |
---|
[3625] | 50 | self.vtk_cells.InsertNextCell(3) |
---|
| 51 | for v in range(3): |
---|
[3958] | 52 | self.vert_index[triangles[n][v]] = verticies[n * 3 + v] |
---|
| 53 | self.vtk_cells.InsertCellPoint(triangles[n][v]) |
---|
[3625] | 54 | |
---|
| 55 | def update_height_quantity(self, quantityName, dynamic=True): |
---|
[3960] | 56 | N_vert = len(self.source.get_vertex_coordinates()) |
---|
[3625] | 57 | qty_index = zeros(N_vert, Float) |
---|
[3960] | 58 | triangles = self.source.get_triangles() |
---|
[3963] | 59 | vertex_values, _ = self.source.get_quantity(quantityName).get_vertex_values(xy=False, smooth=False) |
---|
[3625] | 60 | |
---|
[3960] | 61 | for n in range(len(triangles)): |
---|
[3625] | 62 | for v in range(3): |
---|
[3963] | 63 | #qty_index[triangles[n][v]] = self.source.get_quantity(quantityName).vertex_values[n][v] |
---|
| 64 | qty_index[triangles[n][v]] = vertex_values[n * 3 + v] |
---|
[3625] | 65 | |
---|
| 66 | points = vtkPoints() |
---|
| 67 | for v in range(N_vert): |
---|
| 68 | points.InsertNextPoint(self.vert_index[v][0], |
---|
| 69 | self.vert_index[v][1], |
---|
| 70 | qty_index[v] * self.height_zScales[quantityName] |
---|
| 71 | + self.height_offset[quantityName]) |
---|
[3873] | 72 | if self.xmin == None or self.xmin > self.vert_index[v][0]: |
---|
| 73 | self.xmin = self.vert_index[v][0] |
---|
| 74 | if self.xmax == None or self.xmax < self.vert_index[v][0]: |
---|
| 75 | self.xmax = self.vert_index[v][0] |
---|
| 76 | if self.ymin == None or self.ymin > self.vert_index[v][1]: |
---|
| 77 | self.ymin = self.vert_index[v][1] |
---|
| 78 | if self.ymax == None or self.ymax < self.vert_index[v][1]: |
---|
| 79 | self.ymax = self.vert_index[v][1] |
---|
| 80 | if self.zmin == None or self.zmin > qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName]: |
---|
| 81 | self.zmin = qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName] |
---|
| 82 | if self.zmax == None or self.zmax < qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName]: |
---|
| 83 | self.zmax = qty_index[v] * self.height_zScales[quantityName] + self.height_offset[quantityName] |
---|
[3625] | 84 | |
---|
| 85 | polydata = self.vtk_polyData[quantityName] = vtkPolyData() |
---|
| 86 | polydata.SetPoints(points) |
---|
| 87 | polydata.SetPolys(self.vtk_cells) |
---|
[3873] | 88 | |
---|
| 89 | def get_3d_bounds(self): |
---|
| 90 | return [self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax] |
---|
[3625] | 91 | |
---|
| 92 | def build_quantity_dict(self): |
---|
[3960] | 93 | triangles = self.source.get_triangles() |
---|
[3625] | 94 | quantities = {} |
---|
[3963] | 95 | for q in self.source.get_quantity_names(): |
---|
[3960] | 96 | quantities[q], _ = self.source.get_quantity(q).get_vertex_values(xy=False) |
---|
[3625] | 97 | return quantities |
---|
| 98 | |
---|
[3465] | 99 | def setup_gui(self): |
---|
| 100 | Visualiser.setup_gui(self) |
---|
[3549] | 101 | self.tk_pauseResume = Button(self.tk_controlFrame, text="Pause", command=self.pauseResume) |
---|
[3540] | 102 | self.tk_pauseResume.grid(row=1, column=0, sticky=E+W) |
---|
[3465] | 103 | |
---|
| 104 | def pauseResume(self): |
---|
| 105 | if self.sync_unpaused.isSet(): |
---|
| 106 | self.sync_unpaused.clear() |
---|
| 107 | self.tk_pauseResume.config(text="Resume") |
---|
| 108 | else: |
---|
| 109 | self.sync_unpaused.set() |
---|
| 110 | self.tk_pauseResume.config(text="Pause") |
---|
[3493] | 111 | |
---|
| 112 | def shutdown(self): |
---|
| 113 | Visualiser.shutdown(self) |
---|
| 114 | self.running = False |
---|
| 115 | self.sync_idle.set() |
---|
| 116 | self.sync_unpaused.set() |
---|
[3625] | 117 | |
---|
[3873] | 118 | def redraw(self): |
---|
[3918] | 119 | if self.running and self.sync_unpaused.isSet(): |
---|
[3873] | 120 | self.sync_redrawReady.wait() |
---|
| 121 | self.sync_redrawReady.clear() |
---|
| 122 | self.redraw_quantities() |
---|
| 123 | self.sync_idle.set() |
---|
| 124 | Visualiser.redraw(self) |
---|
| 125 | |
---|
| 126 | def update(self): |
---|
| 127 | """Sync the visualiser to the domain. Call this in the evolve loop.""" |
---|
| 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 | |
---|
| 134 | def evolveFinished(self): |
---|
| 135 | """Stop the visualiser from waiting on signals from the evolve loop. |
---|
| 136 | Call this just after the evolve loop to ensure a clean shutdown.""" |
---|
| 137 | self.running = False |
---|
| 138 | self.sync_redrawReady.set() |
---|