1 /******************************************************************************
2 * src/WSortView.cpp
3 *
4 * WSortView contains both the instrumentated array operators and draws the
5 * displayed visualization.
6 *
7 ******************************************************************************
8 * Copyright (C) 2013-2014 Timo Bingmann <tb@panthema.net>
9 *
10 * This program is free software: you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation, either version 3 of the License, or (at your option)
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
22 *****************************************************************************/
23
24 #include "WSortView.h"
25 #include "SortAlgo.h"
26 #include "WMain.h"
27
28 #include <wx/dcbuffer.h>
29
30 // ****************************************************************************
31 // *** WSortView
32
33 double g_delay = 0;
34
35 bool g_algo_running = false;
36
37 wxString g_algo_name;
38
WSortView(wxWindow * parent,int id,class WMain_wxg * wmain)39 WSortView::WSortView(wxWindow *parent, int id, class WMain_wxg* wmain)
40 : wxPanel(parent, id),
41 wmain(reinterpret_cast<WMain*>(wmain))
42 {
43 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
44
45 m_stepwise = false;
46 m_array.SetSortDelay(this);
47 }
48
~WSortView()49 WSortView::~WSortView()
50 {
51 }
52
53 #if MSW_PERFORMANCECOUNTER
54
mswMicroSleep(int microseconds)55 void mswMicroSleep(int microseconds)
56 {
57 static LARGE_INTEGER s_liFreq = { 0, 0 };
58
59 if (s_liFreq.QuadPart == 0)
60 QueryPerformanceFrequency(&s_liFreq);
61
62 LARGE_INTEGER liStart, liGoal;
63 QueryPerformanceCounter(&liStart);
64
65 liGoal.QuadPart = liStart.QuadPart;
66 liGoal.QuadPart += (s_liFreq.QuadPart * microseconds) / 1000000;
67 LARGE_INTEGER liAct;
68 do
69 {
70 QueryPerformanceCounter(&liAct);
71 if (liStart.QuadPart > liAct.QuadPart) break;
72 } while(liAct.QuadPart < liGoal.QuadPart);
73 }
74
75 #endif // MSW_PERFORMANCECOUNTER
76
DoDelay(double delay)77 void WSortView::DoDelay(double delay)
78 {
79 // must be called by the algorithm thread
80 ASSERT(wmain->m_thread);
81 ASSERT(wxThread::GetCurrentId() == wmain->m_thread->GetId());
82
83 if (wmain->m_thread_terminate)
84 wmain->m_thread->Exit();
85
86 // idle until main thread signals a condition
87 while (m_stepwise)
88 {
89 wxSemaError se = m_step_semaphore.WaitTimeout(200);
90 if (se == wxSEMA_NO_ERROR)
91 break;
92 // else timeout, recheck m_stepwise and loop
93 wmain->m_thread->TestDestroy();
94 wmain->m_thread->Yield();
95 }
96
97 wmain->m_thread->TestDestroy();
98
99 #if __WXGTK__
100 wxMicroSleep(delay * 1000.0);
101 #elif MSW_PERFORMANCECOUNTER
102 mswMicroSleep(delay * 1000.0);
103 #else
104 // wxMSW does not have a high resolution timer, maybe others do?
105 wxMilliSleep(delay);
106 #endif
107 }
108
OnAccess()109 void WSortView::OnAccess()
110 {
111 DoDelay(g_delay);
112 }
113
DoStepwise()114 void WSortView::DoStepwise()
115 {
116 wxSemaError se = m_step_semaphore.Post();
117 if (se != wxSEMA_NO_ERROR) {
118 wxLogError(_T("Error posting to semaphore: %d"), se);
119 }
120 }
121
RepaintNow()122 void WSortView::RepaintNow()
123 {
124 if (!IsShownOnScreen()) return;
125
126 wxClientDC dc(this);
127 wxBufferedDC bdc(&dc, GetSize(), wxBUFFER_CLIENT_AREA);
128 paint(bdc, GetSize());
129 }
130
OnPaint(wxPaintEvent &)131 void WSortView::OnPaint(wxPaintEvent&)
132 {
133 wxAutoBufferedPaintDC dc(this);
134 // on wxMSW, wxAutoBufferedPaintDC holds a bitmap. The bitmaps size =
135 // wxDC's size may not match the window size, thus we pass the window size
136 // explicitly.
137 paint(dc, GetSize());
138 }
139
OnSize(wxSizeEvent &)140 void WSortView::OnSize(wxSizeEvent&)
141 {
142 // full redraw on resize
143 Refresh(false);
144 }
145
paint(wxDC & dc,const wxSize & dcsize)146 void WSortView::paint(wxDC& dc, const wxSize& dcsize)
147 {
148 dc.SetBackground(*wxBLACK_BRUSH);
149 dc.Clear();
150
151 if (m_array.size() == 0) return;
152
153 size_t size = m_array.size();
154
155 size_t fwidth = dcsize.GetWidth();
156 size_t fheight = dcsize.GetHeight();
157
158 size_t width = fwidth - 20;
159 size_t height = fheight - 20;
160
161 dc.SetDeviceOrigin(10,10);
162
163 // *** draw array element bars
164
165 // draw | | | | bars: each bar is width w, separation is w/2
166 // thus n bars need n * w + (n-1) * w/2 width
167
168 // 1st variant: space is 0.5 of bar size
169 //double wbar = width / (size + (size-1) / 2.0);
170 //double bstep = 1.5 * wbar;
171
172 // 2nd variant: one pixel between bars
173 double wbar = (width - (size-1)) / (double)size;
174 if (width <= (size-1)) wbar = 0.0;
175
176 double bstep = wbar + 1.0;
177
178 // special case for bstep = 2 pixel -> draw 2 pixel bars instead of 1px
179 // bar/1px gaps.
180 if ( fabs(wbar - 1.0) < 0.1 && fabs(bstep - 2.0) < 0.1 ) wbar = 2, bstep = 2;
181
182 static const wxPen pens[] = {
183 *wxWHITE_PEN,
184 *wxRED_PEN,
185 *wxGREEN_PEN,
186 *wxCYAN_PEN,
187 wxPen(wxColour(255,255,0)), // 4 yellow
188 wxPen(wxColour(255,0,255)), // 5 magenta
189 wxPen(wxColour(255,192,128)), // 6 orange
190 wxPen(wxColour(255,128,192)), // 7 pink
191 wxPen(wxColour(128,192,255)), // 8 darker cyan
192 wxPen(wxColour(192,255,128)), // 9 darker green
193 wxPen(wxColour(192,128,255)), // 10 purple
194 wxPen(wxColour(128,255,192)), // 11 light green
195 wxPen(wxColour(128,128,255)), // 12 blue
196 wxPen(wxColour(192,128,192)), // 13 dark purple
197 wxPen(wxColour(128,192,192)), // 14 dark cyan
198 wxPen(wxColour(192,192,128)), // 15 dark yellow
199 wxPen(wxColour(0,128,255)), // 16 blue/cyan mix
200 };
201
202 static const wxBrush brushes[] = {
203 *wxWHITE_BRUSH,
204 *wxRED_BRUSH,
205 *wxGREEN_BRUSH,
206 *wxCYAN_BRUSH,
207 wxBrush(wxColour(255,255,0)), // 4 yellow
208 wxBrush(wxColour(255,0,255)), // 5 magenta
209 wxBrush(wxColour(255,192,128)), // 6 orange
210 wxBrush(wxColour(255,128,192)), // 7 pink
211 wxBrush(wxColour(128,192,255)), // 8 darker cyan
212 wxBrush(wxColour(192,255,128)), // 9 darker green
213 wxBrush(wxColour(192,128,255)), // 10 purple
214 wxBrush(wxColour(128,255,192)), // 11 light green
215 wxBrush(wxColour(128,128,255)), // 12 blue
216 wxBrush(wxColour(192,128,192)), // 13 dark purple
217 wxBrush(wxColour(128,192,192)), // 14 dark cyan
218 wxBrush(wxColour(192,192,128)), // 15 dark yellow
219 wxBrush(wxColour(0,128,255)), // 16 blue/cyan mix
220 };
221
222 wxMutexLocker lock(m_array.m_mutex);
223 ASSERT(lock.IsOk());
224
225 for (size_t i = 0; i < size; ++i)
226 {
227 int clr = m_array.GetIndexColor(i);
228
229 ASSERT(clr < (int)(sizeof(brushes) / sizeof(brushes[0])));
230 dc.SetPen( pens[clr] );
231 dc.SetBrush( brushes[clr] );
232
233 dc.DrawRectangle(i*bstep, height,
234 wxMax(1, // draw at least 1 pixel
235 (wxCoord((i+1)*bstep) - wxCoord(i*bstep)) // integral gap to next bar
236 - (bstep - wbar) // space between bars
237 ),
238 -(double)height * m_array.direct(i).get_direct() / m_array.array_max());
239 }
240 }
241
BEGIN_EVENT_TABLE(WSortView,wxWindow)242 BEGIN_EVENT_TABLE(WSortView, wxWindow)
243
244 EVT_PAINT (WSortView::OnPaint)
245 EVT_SIZE (WSortView::OnSize)
246
247 END_EVENT_TABLE()
248
249 // ****************************************************************************
250 // *** Threading
251
252 SortAlgoThread::SortAlgoThread(WMain* wmain, class WSortView& sortview, size_t algo)
253 : wxThread(wxTHREAD_JOINABLE),
254 m_wmain(wmain),
255 m_sortview(sortview),
256 m_algo(algo)
257 {
258 }
259
Entry()260 void* SortAlgoThread::Entry()
261 {
262 ASSERT(m_algo < g_algolist_size);
263 const AlgoEntry& ae = g_algolist[m_algo];
264
265 m_sortview.m_array.OnAlgoLaunch(ae);
266
267 ae.func(m_sortview.m_array);
268
269 m_sortview.m_array.CheckSorted();
270
271 wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, WMain::ID_RUN_FINISHED);
272 m_wmain->GetEventHandler()->AddPendingEvent(evt);
273
274 return NULL;
275 }
276
Exit()277 void SortAlgoThread::Exit()
278 {
279 wxThread::Exit();
280 }
281
282 // ****************************************************************************
283