Sorting Network Verify and Draw
Check Whether Comparator Networks Sort and Draw Them
RenderableComparatorNet.cpp
Go to the documentation of this file.
1
3
4// MIT License
5//
6// Copyright (c) 2022 Ian Parberry
7//
8// Permission is hereby granted, free of charge, to any person obtaining a copy
9// of this software and associated documentation files (the "Software"), to deal
10// in the Software without restriction, including without limitation the rights
11// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12// copies of the Software, and to permit persons to whom the Software is
13// furnished to do so, subject to the following conditions:
14//
15// The above copyright notice and this permission notice shall be included in all
16// copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24// SOFTWARE.
25
26
27#include "Includes.h"
29#include "WindowsHelpers.h"
30
32
34 delete m_pBitmap;
35} //destructor
36
41
43 bool* bUsed = new bool[m_nInputs]; //whether channels are used at the current level
44 Gdiplus::REAL fHeight = m_fYDelta + m_fYDelta2; //height of comparator network so far
45
46 for(UINT i=0; i<m_nDepth; i++){
47 for(UINT j=0; j<m_nInputs; j++) //mark all channels unused
48 bUsed[j] = false;
49
50 //make a pass through the current level printing non-overlapping comparators
51
52 for(UINT pass=0; pass<m_nInputs/2; pass++){
53 bool bPassUsed = false; //whether unprinted comparator found in this level
54
55 for(UINT j=0; j<m_nInputs; j++){ //for rach channel
56 const UINT dest = m_nMatch[i][j]; //other end of comparator
57
58 if(dest < m_nInputs && dest > j && !bUsed[j]){ //can print without overlap
59 bPassUsed = bUsed[j] = bUsed[dest] = true; //mark that we've printed it
60 j = dest; //so that later comparators can't overlap
61 } //if
62 } //for
63
64 if(bPassUsed)fHeight += m_fYDelta; //current pass found a comparator
65 } //for
66
67 fHeight += m_fYDelta2; //next level
68 } //for
69
70 delete [] bUsed;
71 return fHeight;
72} //ComputeBitmapHeight
73
84
86 const UINT src, const UINT dest, const float fDist, bool bRed)
87{
88 float fSrcy = 0, fDesty = 0, fSrcx = 0, fDestx = 0; //end points for PNG, SVG
89 int vx = 0, vy = 0; //axis for TeX
90 const float r = m_fDiameter/2.0f; //circle radius for connectors
91
92 switch(m_eDrawStyle){
93 case eDrawStyle::Vertical:
94 fSrcx = m_fDiameter/2 + src*m_fXDelta;
95 fDestx = m_fDiameter/2 + dest*m_fXDelta;
96 fSrcy = fDesty = fDist;
97 vx = 1; vy = 0;
98 break;
99
100 case eDrawStyle::Horizontal:
101 fSrcx = fDestx = fDist;
102 fSrcy = m_fDiameter/2 + src*m_fXDelta;
103 fDesty = m_fDiameter/2 + dest*m_fXDelta;
104 vx = 0; vy = -1;
105 break;
106 } //switch
107
108 const int nSrcx = (UINT)std::round(fSrcx);
109 const int nSrcy = (UINT)std::round(fSrcy);
110
111 const int nDestx = (UINT)std::round(fDestx);
112 const int nDesty = (UINT)std::round(fDesty);
113
114 switch(m_eExportType){
115 case eExport::Png:
116 if(bRed){
118 const float d = m_fDiameter; //shorthand for diameter
119
120 m_pGraphics->FillEllipse(m_pRedBrush, fSrcx - r, fSrcy - r, d, d);
121 m_pGraphics->FillEllipse(m_pRedBrush, fDestx - r, fDesty - r, d, d);
122 m_pGraphics->DrawLine(m_pRedPen, fSrcx, fSrcy, fDestx, fDesty);
123 } //if
124 } //if
125
126 else{
127 if(m_pGraphics && m_pPen && m_pBrush){
128 const float d = m_fDiameter; //shorthand for diameter
129
130 m_pGraphics->FillEllipse(m_pBrush, fSrcx - r, fSrcy - r, d, d);
131 m_pGraphics->FillEllipse(m_pBrush, fDestx - r, fDesty - r, d, d);
132 m_pGraphics->DrawLine(m_pPen, fSrcx, fSrcy, fDestx, fDesty);
133 } //if
134 } //else
135 break;
136
137 case eExport::Svg:
138 if(m_pOutput){
139 fprintf_s(m_pOutput, "<circle ");
140 if(bRed)fprintf_s(m_pOutput, "style=\"fill:red\" ");
141 fprintf_s(m_pOutput, "cx=\"%d\" cy=\"%d\"/>", nSrcx, nSrcy);
142
143 fprintf_s(m_pOutput, "<circle ");
144 if(bRed)fprintf_s(m_pOutput, "style=\"fill:red\" ");
145 fprintf_s(m_pOutput, "cx=\"%d\" cy=\"%d\"/>", nDestx, nDesty);
146
147 fprintf_s(m_pOutput, "<line ");
148 if(bRed)fprintf_s(m_pOutput, "style=\"stroke:red\" ");
149 fprintf_s(m_pOutput, "x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\"/>\n",
150 nSrcx, nSrcy, nDestx, nDesty);
151 } //if
152 break;
153
154 case eExport::TeX:
155 if(m_pOutput){
156 const int d = (UINT)std::round(m_fDiameter);
157 const UINT nLen = abs(nDestx - nSrcx + nDesty - nSrcy);
158
159 fprintf_s(m_pOutput, "\\put(%d,-%d){\\circle*{%d}}\n", nSrcx, nSrcy, d);
160 fprintf_s(m_pOutput, "\\put(%d,-%d){\\circle*{%d}}\n", nDestx, nDesty, d);
161 fprintf_s(m_pOutput, "\\put(%d,-%d){\\line(%d,%d){%u}}\n", nDestx, nDesty,
162 vx, vy, nLen);
163 } //if
164 break;
165 } //switch
166} //DrawComparator
167
175
177 bool* bUsed = new bool[m_nInputs]; //whether channels are used at the current level
178 float fLen = m_fYDelta + m_fYDelta2; //distance along channel
179
180 for(UINT i=0; i<m_nDepth; i++){ //for each level
181 for(UINT j=0; j<m_nInputs; j++) //mark all channels unused
182 bUsed[j] = false;
183
184 //make a pass through the current level printing comparators that don't overlap
185
186 for(UINT pass=0; pass<m_nInputs/2; pass++){
187 bool bPassUsed = false; //whether we found an unprinted comparator in this level
188
189 switch(m_eDrawStyle){
190 case eDrawStyle::Vertical:
191 for(UINT j=0; j<m_nInputs; j++){
192 const UINT dest = m_nMatch[i][j];
193
194 if(dest < m_nInputs && dest > j && !bUsed[j]){ //can print without overlap
195 const bool bRed = m_bSorts && !m_bUsed[i][j]; //whether to be drawn red
196 DrawComparator(dest, j, fLen, bRed); //draw comparator
197 bPassUsed = bUsed[j] = bUsed[dest] = true; //mark that we've printed it
198 j = dest; //so that later comparators can't overlap
199 } //if
200 } //for
201 break;
202
203 case eDrawStyle::Horizontal:
204 for(int j=m_nInputs-1; j>=0; j--){
205 const UINT dest = m_nMatch[i][j];
206
207 if(dest < m_nInputs && dest < (UINT)j && !bUsed[dest]){ //can print without overlap
208 const bool bRed = m_bSorts && !m_bUsed[i][j]; //whether to be drawn red
209 DrawComparator(j, dest, fLen, bRed); //draw comparator
210 bPassUsed = bUsed[j] = bUsed[dest] = true; //mark that we've printed it
211 j = dest; //so that later comparators can't overlap
212 } //if
213 } //for
214 break;
215 } //switch
216
217 if(bPassUsed)fLen += m_fYDelta; //the current pass found a comparator
218 } //for
219
220 fLen += m_fYDelta2; //next level
221 } //for
222
223 delete [] bUsed;
224} //DrawComparators
225
233
235 float fSrcy = 0, fDesty = 0, fSrcx = 0, fDestx = 0; //end points for PNG, SVG
236 int vx = 0, vy = 0; //axis for TeX
237
238 switch(m_eDrawStyle){
239 case eDrawStyle::Vertical:
240 fSrcy = 0.0f; fDesty = fLen;
241 fSrcx = m_fDiameter/2; fDestx = fSrcx;
242 vx = 0; vy = -1;
243 break;
244
245 case eDrawStyle::Horizontal:
246 fSrcx = 0.0f; fDestx = fLen;
247 fSrcy = m_fDiameter/2; fDesty = fSrcy;
248 vx = 1; vy = 0;
249 break;
250 } //switch
251
252 const int nLen = (UINT)std::round(fLen);
253
254 for(UINT i=0; i<m_nInputs; i++){
255 const int nSrcx = (UINT)std::round(fSrcx);
256 const int nSrcy = (UINT)std::round(fSrcy);
257
258 const int nDestx = (UINT)std::round(fDestx);
259 const int nDesty = (UINT)std::round(fDesty);
260
261 switch(m_eExportType){
262 case eExport::Png:
263 if(m_pGraphics) //safety
264 m_pGraphics->DrawLine(m_pPen, fSrcx, fSrcy, fDestx, fDesty);
265 break;
266
267 case eExport::Svg:
268 if(m_pOutput) //safety
269 fprintf_s(m_pOutput, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\"/>\n",
270 nSrcx, nSrcy, nDestx, nDesty);
271 break;
272
273 case eExport::TeX:
274 if(m_pOutput) //safety
275 fprintf_s(m_pOutput, "\\put(%d,-%d){\\line(%d,%d){%d}}\n",
276 nSrcx, nSrcy, vx, vy, nLen);
277 break;
278 } //switch
279
280 switch(m_eDrawStyle){
281 case eDrawStyle::Vertical: fSrcx += m_fXDelta; fDestx = fSrcx; break;
282 case eDrawStyle::Horizontal: fSrcy += m_fXDelta; fDesty = fSrcy; break;
283 } //switch
284 } //for
285} //DrawChannels
286
294
296
298 m_eExportType = eExport::Png;
299 m_eDrawStyle = d;
300
301 const UINT w = (UINT)std::ceil((m_nInputs - 1)*m_fXDelta + m_fPenWidth + m_fDiameter);
302 const UINT h = (UINT)std::ceil(ComputeBitmapHeight());
303
304 switch(m_eDrawStyle){
305 case eDrawStyle::Vertical: m_pBitmap = new Gdiplus::Bitmap(w, h); break;
306 case eDrawStyle::Horizontal: m_pBitmap = new Gdiplus::Bitmap(h, w); break;
307 } //switch
308
309 m_pGraphics = new Gdiplus::Graphics(m_pBitmap);
310 m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
311 m_pGraphics->Clear(Gdiplus::Color::Transparent);
312 m_pPen = new Gdiplus::Pen(Gdiplus::Color::Black);
313 m_pRedPen = new Gdiplus::Pen(Gdiplus::Color::Red);
314 m_pPen->SetWidth(m_fPenWidth);
315 m_pRedPen->SetWidth(m_fPenWidth);
316 m_pBrush = new Gdiplus::SolidBrush(Gdiplus::Color::Black);
317 m_pRedBrush = new Gdiplus::SolidBrush(Gdiplus::Color::Red);
318
319 DrawChannels((float)h);
321
322 delete m_pGraphics; m_pGraphics = nullptr;
323 delete m_pPen; m_pPen = nullptr;
324 delete m_pRedPen; m_pRedPen = nullptr;
325 delete m_pBrush; m_pBrush = nullptr;
326 delete m_pRedBrush; m_pRedBrush = nullptr;
327} //Draw
328
333
335 CLSID clsid; //for PNG class id
336 HRESULT hr = GetEncoderClsid((WCHAR*)L"image/png", &clsid);
337
338 if(SUCCEEDED(hr)){
339 if(m_pBitmap)
340 m_pBitmap->Save(lpwstr, &clsid, nullptr);
341 else hr = E_FAIL;
342 } //if
343
344 return hr;
345} //ExportToPNG
346
353
355 _wfopen_s(&m_pOutput, lpwstr, L"wt");
356
357 if(m_pOutput){
358 m_eExportType = eExport::TeX;
359
360 const UINT w = (UINT)std::ceil((m_nInputs - 1)*m_fXDelta + m_fPenWidth + m_fDiameter);
361 const UINT h = (UINT)std::ceil(ComputeBitmapHeight());
362
363 fprintf_s(m_pOutput, "\\setlength{\\unitlength}{0.5pt}\n");
364
365 switch(m_eDrawStyle){
366 case eDrawStyle::Vertical:
367 fprintf_s(m_pOutput, "\\begin{picture}(%u,%u)(0,-%u)\n", w, h, h);
368 break;
369
370 case eDrawStyle::Horizontal:
371 fprintf_s(m_pOutput, "\\begin{picture}(%u,%u)(0,-%u)\n", h, w, w);
372 break;
373 } //switch
374
375 fprintf_s(m_pOutput, "\\thicklines\n");
376
377 //start drawing
378
379 DrawChannels((float)h);
381
382 //end drawing
383
384 fprintf_s(m_pOutput, "\\end{picture}\n"); //close the picture environment
385
386 //clean up and exit
387
388 fclose(m_pOutput);
389 m_pOutput = nullptr; //safety
390 return S_OK;
391 } //if
392
393 return E_FAIL;
394} //ExportToTex
395
402
404 _wfopen_s(&m_pOutput, lpwstr, L"wt");
405
406 if(m_pOutput){
407 m_eExportType = eExport::Svg;
408
409 fprintf_s(m_pOutput, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); //header
410
411 const UINT w = (UINT)std::ceil((m_nInputs - 1)*m_fXDelta +
413 const UINT h = (UINT)std::ceil(ComputeBitmapHeight());
414
415 //svg tag
416
417 switch(m_eDrawStyle){
418 case eDrawStyle::Vertical:
419 fprintf_s(m_pOutput, "<svg width=\"%u\" height=\"%u\" ", w + 8, h + 8);
420 fprintf_s(m_pOutput, "viewBox=\"-4 -4 %u %u\" ", w + 8, h + 8);
421 break;
422
423 case eDrawStyle::Horizontal:
424 fprintf_s(m_pOutput, "<svg width=\"%u\" height=\"%u\" ", h + 8, w + 8);
425 fprintf_s(m_pOutput, "viewBox=\"-4 -4 %u %u\" ", h + 8, w + 8);
426 break;
427 } //switch
428
429 fprintf_s(m_pOutput, "xmlns=\"http://www.w3.org/2000/svg\">\n");
430
431 //style tag
432
433 fprintf_s(m_pOutput, "<style>\n");
434 fprintf_s(m_pOutput, "circle{fill:black;r:%0.1f}", m_fDiameter/2.0f);
435 fprintf_s(m_pOutput, "line{stroke:black;stroke-width:%0.1f}\n", m_fPenWidth);
436 fprintf_s(m_pOutput, "</style>\n");
437
438 //start drawing
439
440 DrawChannels((float)h);
442
443 //end drawing
444
445 fprintf_s(m_pOutput, "</svg>\n"); //close the svg tag
446
447 //clean up and exit
448
449 fclose(m_pOutput);
450 m_pOutput = nullptr; //safety
451 return S_OK;
452 } //if
453
454 return E_FAIL;
455} //ExportToSVG
456
459
461 return m_pBitmap;
462} //GetBitmap
eDrawStyle
Drawing style.
Definition: Defines.h:34
Useful includes.
Interface for the renderable comparator network CRenderableComparatorNet.
HRESULT GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
Get encoder CLSID.
Interface for some helpful Windows-specific functions.
UINT ** m_nMatch
Matchings at each level.
bool m_bSorts
True if it sorts, false if it doesn't or unknown.
UINT m_nInputs
Number of inputs.
bool ** m_bUsed
Whether comparators are used when sorting.
const Gdiplus::REAL m_fYDelta2
Extra vertical gap between layers in pixels.
HRESULT ExportToSVG(LPWSTR)
Export in SVG format.
void DrawComparator(const UINT, const UINT, const float, bool=false)
Draw a comparator.
Gdiplus::Pen * m_pRedPen
Pointer to graphics pen.
HRESULT ExportToTex(LPWSTR)
Export in TeX format.
Gdiplus::Bitmap * GetBitmap()
Get bitmap pointer.
Gdiplus::SolidBrush * m_pBrush
Pointer to graphics brush.
const Gdiplus::REAL m_fPenWidth
Pen width in pixels.
HRESULT ExportToPNG(LPWSTR)
Export in PNG format.
Gdiplus::Bitmap * m_pBitmap
Pointer to a bitmap image.
const Gdiplus::REAL m_fDiameter
Diameter of circles in pixels.
Gdiplus::REAL ComputeBitmapHeight()
Compute bitmap height.
Gdiplus::SolidBrush * m_pRedBrush
Pointer to graphics brush.
Gdiplus::Pen * m_pPen
Pointer to graphics pen.
Gdiplus::Graphics * m_pGraphics
Pointer to graphics object.
void DrawChannels(const float fLen)
Draw channels.
const Gdiplus::REAL m_fXDelta
Gap between channels in pixels.
const Gdiplus::REAL m_fYDelta
Vertical comparator gap in pixels.
eDrawStyle m_eDrawStyle
Drawing style.
void Draw(const eDrawStyle)
Draw to a Gdiplus::Bitmap.
eExport m_eExportType
Export type.
void DrawComparators()
Draw all comparators.