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