1 | from Numeric import Float, zeros, shape |
---|
2 | from Tkinter import Button, E, Tk, W |
---|
3 | from threading import Event |
---|
4 | from visualiser import Visualiser |
---|
5 | from vtk import vtkCellArray, vtkPoints, vtkPolyData |
---|
6 | |
---|
7 | class RealtimeVisualiser(Visualiser): |
---|
8 | """A VTK-powered realtime 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 | 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. |
---|
18 | """ |
---|
19 | def __init__(self, source): |
---|
20 | """The source parameter is assumed to be a Domain. |
---|
21 | """ |
---|
22 | Visualiser.__init__(self, source) |
---|
23 | |
---|
24 | self.running = True |
---|
25 | |
---|
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 | |
---|
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() |
---|
40 | |
---|
41 | def setup_grid(self): |
---|
42 | self.vtk_cells = vtkCellArray() |
---|
43 | triangles = self.source.get_triangles() |
---|
44 | N_tri = len(self.source) |
---|
45 | verticies = self.source.get_vertex_coordinates() |
---|
46 | N_vert = len(verticies) |
---|
47 | |
---|
48 | # Also build vert_index - a list of the x & y values of each vertex |
---|
49 | self.vert_index = zeros((N_vert,2), Float) |
---|
50 | for n in range(N_tri): |
---|
51 | self.vtk_cells.InsertNextCell(3) |
---|
52 | for v in range(3): |
---|
53 | self.vert_index[n * 3 + v] = verticies[n * 3 + v] |
---|
54 | self.vtk_cells.InsertCellPoint(n * 3 + v) |
---|
55 | |
---|
56 | def update_height_quantity(self, quantityName, dynamic=True): |
---|
57 | N_vert = len(self.source.get_vertex_coordinates()) |
---|
58 | qty_index = zeros(N_vert, Float) |
---|
59 | triangles = self.source.get_triangles() |
---|
60 | vertex_values, _ = self.source.get_quantity(quantityName).get_vertex_values(xy=False, smooth=False) |
---|
61 | |
---|
62 | for n in range(N_vert): |
---|
63 | qty_index[n] = vertex_values[n] |
---|
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]) |
---|
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] |
---|
83 | |
---|
84 | polydata = self.vtk_polyData[quantityName] = vtkPolyData() |
---|
85 | polydata.SetPoints(points) |
---|
86 | polydata.SetPolys(self.vtk_cells) |
---|
87 | |
---|
88 | def get_3d_bounds(self): |
---|
89 | return [self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax] |
---|
90 | |
---|
91 | def build_quantity_dict(self): |
---|
92 | triangles = self.source.get_triangles() |
---|
93 | quantities = {} |
---|
94 | for q in self.source.get_quantity_names(): |
---|
95 | quantities[q], _ = self.source.get_quantity(q).get_vertex_values(xy=False) |
---|
96 | return quantities |
---|
97 | |
---|
98 | def setup_gui(self): |
---|
99 | Visualiser.setup_gui(self) |
---|
100 | self.tk_pauseResume = Button(self.tk_controlFrame, text="Pause", command=self.pauseResume) |
---|
101 | self.tk_pauseResume.grid(row=1, column=0, sticky=E+W) |
---|
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") |
---|
110 | |
---|
111 | def shutdown(self): |
---|
112 | Visualiser.shutdown(self) |
---|
113 | self.running = False |
---|
114 | self.sync_idle.set() |
---|
115 | self.sync_unpaused.set() |
---|
116 | |
---|
117 | def redraw(self): |
---|
118 | if self.running and self.sync_unpaused.isSet(): |
---|
119 | self.sync_redrawReady.wait() |
---|
120 | self.sync_redrawReady.clear() |
---|
121 | self.redraw_quantities() |
---|
122 | self.sync_idle.set() |
---|
123 | Visualiser.redraw(self) |
---|
124 | |
---|
125 | def update(self,pause=False): |
---|
126 | """Sync the visualiser to the domain. Call this in the evolve loop.""" |
---|
127 | |
---|
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 | 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 | |
---|
144 | |
---|
145 | |
---|
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() |
---|