]> git.saurik.com Git - wxWidgets.git/blame - src/common/dcbase.cpp
added missing include; minor reformatting
[wxWidgets.git] / src / common / dcbase.cpp
CommitLineData
dbe94982 1/////////////////////////////////////////////////////////////////////////////
1e6feb95
VZ
2// Name: common/dcbase.cpp
3// Purpose: generic methods of the wxDC Class
4// Author: Vadim Zeitlin
dbe94982
BM
5// Modified by:
6// Created: 05/25/99
7// RCS-ID: $Id$
77ffb593 8// Copyright: (c) wxWidgets team
65571936 9// Licence: wxWindows licence
dbe94982
BM
10/////////////////////////////////////////////////////////////////////////////
11
1e6feb95
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
1e6feb95
VZ
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
dbe94982
BM
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
bd1e9c12 24 #pragma hdrstop
dbe94982
BM
25#endif
26
dbe94982 27#include "wx/dc.h"
c77a6796 28#include "wx/math.h"
dbe94982 29
68379eaf 30// bool wxDCBase::sm_cacheing = false;
0cbff120 31
1e6feb95
VZ
32// ============================================================================
33// implementation
34// ============================================================================
35
36// ----------------------------------------------------------------------------
37// special symbols
38// ----------------------------------------------------------------------------
39
cd9da200
VZ
40void wxDCBase::DoDrawCheckMark(wxCoord x1, wxCoord y1,
41 wxCoord width, wxCoord height)
42{
43 wxCHECK_RET( Ok(), wxT("invalid window dc") );
44
45 wxCoord x2 = x1 + width,
46 y2 = y1 + height;
47
48 // this is to yield width of 3 for width == height == 10
49 SetPen(wxPen(GetTextForeground(), (width + height + 1) / 7, wxSOLID));
50
51 // we're drawing a scaled version of wx/generic/tick.xpm here
52 wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
53 y3 = y1 + height / 2; // y of the left tick branch
54 DoDrawLine(x1, y3, x3, y2);
55 DoDrawLine(x3, y2, x2, y1);
56
57 CalcBoundingBox(x1, y1);
58 CalcBoundingBox(x2, y2);
59}
60
1e6feb95
VZ
61// ----------------------------------------------------------------------------
62// line/polygons
63// ----------------------------------------------------------------------------
64
72cdf4c9 65void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
dbe94982 66{
b1d4dd7a 67 int n = list->GetCount();
dbe94982
BM
68 wxPoint *points = new wxPoint[n];
69
70 int i = 0;
222ed1d6 71 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
dbe94982 72 {
b1d4dd7a 73 wxPoint *point = (wxPoint *)node->GetData();
dbe94982
BM
74 points[i].x = point->x;
75 points[i].y = point->y;
76 }
77
78 DoDrawLines(n, points, xoffset, yoffset);
79
80 delete [] points;
81}
82
83
84void wxDCBase::DrawPolygon(const wxList *list,
72cdf4c9 85 wxCoord xoffset, wxCoord yoffset,
dbe94982
BM
86 int fillStyle)
87{
b1d4dd7a 88 int n = list->GetCount();
dbe94982
BM
89 wxPoint *points = new wxPoint[n];
90
91 int i = 0;
222ed1d6 92 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
dbe94982 93 {
b1d4dd7a 94 wxPoint *point = (wxPoint *)node->GetData();
dbe94982
BM
95 points[i].x = point->x;
96 points[i].y = point->y;
97 }
98
99 DoDrawPolygon(n, points, xoffset, yoffset, fillStyle);
100
101 delete [] points;
102}
103
63b9e659
VZ
104void
105wxDCBase::DoDrawPolyPolygon(int n,
793db755 106 int count[],
63b9e659
VZ
107 wxPoint points[],
108 wxCoord xoffset, wxCoord yoffset,
109 int fillStyle)
110{
111 if ( n == 1 )
112 {
793db755 113 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
63b9e659
VZ
114 return;
115 }
116
117 int i, j, lastOfs;
118 wxPoint* pts;
119 wxPen pen;
120
121 for (i = j = lastOfs = 0; i < n; i++)
122 {
123 lastOfs = j;
793db755 124 j += count[i];
63b9e659
VZ
125 }
126 pts = new wxPoint[j+n-1];
127 for (i = 0; i < j; i++)
128 pts[i] = points[i];
129 for (i = 2; i <= n; i++)
130 {
793db755 131 lastOfs -= count[n-i];
63b9e659
VZ
132 pts[j++] = pts[lastOfs];
133 }
134
135 pen = GetPen();
136 SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT));
137 DoDrawPolygon(j, pts, xoffset, yoffset, fillStyle);
138 SetPen(pen);
139 for (i = j = 0; i < n; i++)
140 {
793db755
VZ
141 DoDrawLines(count[i], pts+j, xoffset, yoffset);
142 j += count[i];
63b9e659 143 }
6db1f43b 144 delete[] pts;
63b9e659
VZ
145}
146
1e6feb95
VZ
147// ----------------------------------------------------------------------------
148// splines
149// ----------------------------------------------------------------------------
dbe94982 150
88ac883a 151#if wxUSE_SPLINES
dbe94982
BM
152
153// TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
72cdf4c9 154void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
dbe94982
BM
155{
156 wxList point_list;
157
158 wxPoint *point1 = new wxPoint;
159 point1->x = x1; point1->y = y1;
160 point_list.Append((wxObject*)point1);
161
162 wxPoint *point2 = new wxPoint;
163 point2->x = x2; point2->y = y2;
164 point_list.Append((wxObject*)point2);
165
166 wxPoint *point3 = new wxPoint;
167 point3->x = x3; point3->y = y3;
168 point_list.Append((wxObject*)point3);
169
170 DrawSpline(&point_list);
171
222ed1d6 172 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
dbe94982 173 {
b1d4dd7a 174 wxPoint *p = (wxPoint *)node->GetData();
dbe94982
BM
175 delete p;
176 }
177}
178
179void wxDCBase::DrawSpline(int n, wxPoint points[])
180{
181 wxList list;
182 for (int i =0; i < n; i++)
183 {
184 list.Append((wxObject*)&points[i]);
185 }
186
187 DrawSpline(&list);
188}
189
fe2e4366
VS
190// ----------------------------------- spline code ----------------------------------------
191
192void wx_quadratic_spline(double a1, double b1, double a2, double b2,
193 double a3, double b3, double a4, double b4);
194void wx_clear_stack();
195int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
196 double *y3, double *x4, double *y4);
197void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
198 double x4, double y4);
199static bool wx_spline_add_point(double x, double y);
200static void wx_spline_draw_point_array(wxDCBase *dc);
201
202wxList wx_spline_point_list;
203
204#define half(z1, z2) ((z1+z2)/2.0)
205#define THRESHOLD 5
206
207/* iterative version */
208
209void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
210 double b4)
211{
212 register double xmid, ymid;
213 double x1, y1, x2, y2, x3, y3, x4, y4;
214
215 wx_clear_stack();
216 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
217
218 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
219 xmid = (double)half(x2, x3);
220 ymid = (double)half(y2, y3);
221 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
222 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
223 wx_spline_add_point( x1, y1 );
224 wx_spline_add_point( xmid, ymid );
225 } else {
226 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
227 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
228 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
229 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
230 }
231 }
232}
233
234/* utilities used by spline drawing routines */
235
236typedef struct wx_spline_stack_struct {
237 double x1, y1, x2, y2, x3, y3, x4, y4;
238} Stack;
239
240#define SPLINE_STACK_DEPTH 20
241static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
242static Stack *wx_stack_top;
243static int wx_stack_count;
244
245void wx_clear_stack()
246{
247 wx_stack_top = wx_spline_stack;
248 wx_stack_count = 0;
249}
250
251void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
252{
253 wx_stack_top->x1 = x1;
254 wx_stack_top->y1 = y1;
255 wx_stack_top->x2 = x2;
256 wx_stack_top->y2 = y2;
257 wx_stack_top->x3 = x3;
258 wx_stack_top->y3 = y3;
259 wx_stack_top->x4 = x4;
260 wx_stack_top->y4 = y4;
261 wx_stack_top++;
262 wx_stack_count++;
263}
264
265int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
266 double *x3, double *y3, double *x4, double *y4)
267{
268 if (wx_stack_count == 0)
269 return (0);
270 wx_stack_top--;
271 wx_stack_count--;
272 *x1 = wx_stack_top->x1;
273 *y1 = wx_stack_top->y1;
274 *x2 = wx_stack_top->x2;
275 *y2 = wx_stack_top->y2;
276 *x3 = wx_stack_top->x3;
277 *y3 = wx_stack_top->y3;
278 *x4 = wx_stack_top->x4;
279 *y4 = wx_stack_top->y4;
280 return (1);
281}
282
283static bool wx_spline_add_point(double x, double y)
284{
285 wxPoint *point = new wxPoint ;
286 point->x = (int) x;
287 point->y = (int) y;
288 wx_spline_point_list.Append((wxObject*)point);
68379eaf 289 return true;
fe2e4366
VS
290}
291
292static void wx_spline_draw_point_array(wxDCBase *dc)
293{
294 dc->DrawLines(&wx_spline_point_list, 0, 0 );
222ed1d6 295 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
fe2e4366
VS
296 while (node)
297 {
b1d4dd7a 298 wxPoint *point = (wxPoint *)node->GetData();
fe2e4366 299 delete point;
222ed1d6 300 wx_spline_point_list.Erase(node);
b1d4dd7a 301 node = wx_spline_point_list.GetFirst();
fe2e4366
VS
302 }
303}
304
305void wxDCBase::DoDrawSpline( wxList *points )
306{
307 wxCHECK_RET( Ok(), wxT("invalid window dc") );
308
309 wxPoint *p;
310 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
311 double x1, y1, x2, y2;
312
222ed1d6 313 wxList::compatibility_iterator node = points->GetFirst();
55e3f1c4 314 if (node == wxList::compatibility_iterator())
410bb8c1
RD
315 // empty list
316 return;
e0d31471 317
b1d4dd7a 318 p = (wxPoint *)node->GetData();
fe2e4366
VS
319
320 x1 = p->x;
321 y1 = p->y;
322
b1d4dd7a
RL
323 node = node->GetNext();
324 p = (wxPoint *)node->GetData();
fe2e4366
VS
325
326 x2 = p->x;
327 y2 = p->y;
328 cx1 = (double)((x1 + x2) / 2);
329 cy1 = (double)((y1 + y2) / 2);
330 cx2 = (double)((cx1 + x2) / 2);
331 cy2 = (double)((cy1 + y2) / 2);
332
333 wx_spline_add_point(x1, y1);
334
28b4db7f
VZ
335 while ((node = node->GetNext())
336#if !wxUSE_STL
337 != NULL
338#endif // !wxUSE_STL
339 )
fe2e4366 340 {
b1d4dd7a 341 p = (wxPoint *)node->GetData();
fe2e4366
VS
342 x1 = x2;
343 y1 = y2;
344 x2 = p->x;
345 y2 = p->y;
346 cx4 = (double)(x1 + x2) / 2;
347 cy4 = (double)(y1 + y2) / 2;
348 cx3 = (double)(x1 + cx4) / 2;
349 cy3 = (double)(y1 + cy4) / 2;
350
351 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
352
353 cx1 = cx4;
354 cy1 = cy4;
355 cx2 = (double)(cx1 + x2) / 2;
356 cy2 = (double)(cy1 + y2) / 2;
357 }
358
359 wx_spline_add_point( cx1, cy1 );
360 wx_spline_add_point( x2, y2 );
361
362 wx_spline_draw_point_array( this );
363}
364
88ac883a 365#endif // wxUSE_SPLINES
1e6feb95 366
0919e93e
RD
367// ----------------------------------------------------------------------------
368// Partial Text Extents
369// ----------------------------------------------------------------------------
370
371
174ee1b3 372// Each element of the widths array will be the width of the string up to and
3103e8a9 373// including the corresponding character in text. This is the generic
0919e93e 374// implementation, the port-specific classes should do this with native APIs
174ee1b3
RD
375// if available and if faster. Note: pango_layout_index_to_pos is much slower
376// than calling GetTextExtent!!
377
06fe86b7 378#define FWC_SIZE 256
174ee1b3
RD
379
380class FontWidthCache
381{
382public:
383 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
384 ~FontWidthCache() { delete []m_widths; }
2aaa050c
VZ
385
386 void Reset()
387 {
388 if (!m_widths)
389 m_widths = new int[FWC_SIZE];
390
391 memset(m_widths, 0, sizeof(int)*FWC_SIZE);
392 }
393
174ee1b3
RD
394 wxFont m_font;
395 double m_scaleX;
396 int *m_widths;
397};
398
399static FontWidthCache s_fontWidthCache;
0919e93e
RD
400
401bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
402{
403 int totalWidth = 0;
0919e93e 404
c77a6796 405 const size_t len = text.Length();
0919e93e 406 widths.Empty();
174ee1b3 407 widths.Add(0, len);
2aaa050c 408
174ee1b3 409 // reset the cache if font or horizontal scale have changed
c77a6796
VZ
410 if ( !s_fontWidthCache.m_widths ||
411 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
412 (s_fontWidthCache.m_font != GetFont()) )
174ee1b3
RD
413 {
414 s_fontWidthCache.Reset();
415 s_fontWidthCache.m_font = GetFont();
416 s_fontWidthCache.m_scaleX = m_scaleX;
417 }
418
0919e93e
RD
419 // Calculate the position of each character based on the widths of
420 // the previous characters
c77a6796
VZ
421 int w, h;
422 for ( size_t i = 0; i < len; i++ )
174ee1b3
RD
423 {
424 const wxChar c = text[i];
425 unsigned int c_int = (unsigned int)c;
426
2aaa050c 427 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
174ee1b3
RD
428 {
429 w = s_fontWidthCache.m_widths[c_int];
430 }
2aaa050c 431 else
174ee1b3
RD
432 {
433 GetTextExtent(c, &w, &h);
434 if (c_int < FWC_SIZE)
435 s_fontWidthCache.m_widths[c_int] = w;
436 }
437
0919e93e
RD
438 totalWidth += w;
439 widths[i] = totalWidth;
440 }
2aaa050c 441
0919e93e
RD
442 return true;
443}
444
445
1e6feb95
VZ
446// ----------------------------------------------------------------------------
447// enhanced text drawing
448// ----------------------------------------------------------------------------
449
450void wxDCBase::GetMultiLineTextExtent(const wxString& text,
451 wxCoord *x,
452 wxCoord *y,
453 wxCoord *h,
454 wxFont *font)
455{
c7aaa64f
VS
456 wxCoord widthTextMax = 0, widthLine,
457 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
1e6feb95
VZ
458
459 wxString curLine;
460 for ( const wxChar *pc = text; ; pc++ )
461 {
462 if ( *pc == _T('\n') || *pc == _T('\0') )
463 {
464 if ( curLine.empty() )
465 {
466 // we can't use GetTextExtent - it will return 0 for both width
467 // and height and an empty line should count in height
468 // calculation
469
470 // assume that this line has the same height as the previous
471 // one
472 if ( !heightLineDefault )
473 heightLineDefault = heightLine;
474
475 if ( !heightLineDefault )
476 {
477 // but we don't know it yet - choose something reasonable
478 GetTextExtent(_T("W"), NULL, &heightLineDefault,
479 NULL, NULL, font);
480 }
481
482 heightTextTotal += heightLineDefault;
483 }
484 else
485 {
486 GetTextExtent(curLine, &widthLine, &heightLine,
487 NULL, NULL, font);
488 if ( widthLine > widthTextMax )
489 widthTextMax = widthLine;
490 heightTextTotal += heightLine;
491 }
492
493 if ( *pc == _T('\n') )
494 {
495 curLine.clear();
496 }
497 else
498 {
499 // the end of string
500 break;
501 }
502 }
503 else
504 {
505 curLine += *pc;
506 }
507 }
508
509 if ( x )
510 *x = widthTextMax;
511 if ( y )
512 *y = heightTextTotal;
513 if ( h )
514 *h = heightLine;
515}
516
517void wxDCBase::DrawLabel(const wxString& text,
518 const wxBitmap& bitmap,
519 const wxRect& rect,
520 int alignment,
521 int indexAccel,
522 wxRect *rectBounding)
523{
524 // find the text position
525 wxCoord widthText, heightText, heightLine;
526 GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
527
528 wxCoord width, height;
529 if ( bitmap.Ok() )
530 {
531 width = widthText + bitmap.GetWidth();
532 height = bitmap.GetHeight();
533 }
534 else // no bitmap
535 {
536 width = widthText;
537 height = heightText;
538 }
539
540 wxCoord x, y;
541 if ( alignment & wxALIGN_RIGHT )
542 {
543 x = rect.GetRight() - width;
544 }
545 else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
546 {
547 x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
548 }
549 else // alignment & wxALIGN_LEFT
550 {
551 x = rect.GetLeft();
552 }
553
554 if ( alignment & wxALIGN_BOTTOM )
555 {
556 y = rect.GetBottom() - height;
557 }
558 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
559 {
560 y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
561 }
562 else // alignment & wxALIGN_TOP
563 {
564 y = rect.GetTop();
565 }
566
567 // draw the bitmap first
568 wxCoord x0 = x,
569 y0 = y,
570 width0 = width;
571 if ( bitmap.Ok() )
572 {
68379eaf 573 DrawBitmap(bitmap, x, y, true /* use mask */);
1e6feb95
VZ
574
575 wxCoord offset = bitmap.GetWidth() + 4;
576 x += offset;
577 width -= offset;
578
579 y += (height - heightText) / 2;
580 }
581
582 // we will draw the underscore under the accel char later
583 wxCoord startUnderscore = 0,
584 endUnderscore = 0,
585 yUnderscore = 0;
586
587 // split the string into lines and draw each of them separately
588 wxString curLine;
589 for ( const wxChar *pc = text; ; pc++ )
590 {
591 if ( *pc == _T('\n') || *pc == _T('\0') )
592 {
593 int xRealStart = x; // init it here to avoid compielr warnings
594
595 if ( !curLine.empty() )
596 {
597 // NB: can't test for !(alignment & wxALIGN_LEFT) because
598 // wxALIGN_LEFT is 0
599 if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
600 {
601 wxCoord widthLine;
602 GetTextExtent(curLine, &widthLine, NULL);
603
604 if ( alignment & wxALIGN_RIGHT )
605 {
606 xRealStart += width - widthLine;
607 }
608 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
609 {
610 xRealStart += (width - widthLine) / 2;
611 }
612 }
613 //else: left aligned, nothing to do
614
615 DrawText(curLine, xRealStart, y);
616 }
617
618 y += heightLine;
619
620 // do we have underscore in this line? we can check yUnderscore
621 // because it is set below to just y + heightLine if we do
622 if ( y == yUnderscore )
623 {
624 // adjust the horz positions to account for the shift
625 startUnderscore += xRealStart;
626 endUnderscore += xRealStart;
627 }
628
629 if ( *pc == _T('\0') )
630 break;
631
632 curLine.clear();
633 }
634 else // not end of line
635 {
328bfc22 636 if ( pc - text.c_str() == indexAccel )
1e6feb95
VZ
637 {
638 // remeber to draw underscore here
639 GetTextExtent(curLine, &startUnderscore, NULL);
640 curLine += *pc;
641 GetTextExtent(curLine, &endUnderscore, NULL);
642
643 yUnderscore = y + heightLine;
644 }
645 else
646 {
647 curLine += *pc;
648 }
649 }
650 }
651
652 // draw the underscore if found
653 if ( startUnderscore != endUnderscore )
654 {
655 // it should be of the same colour as text
656 SetPen(wxPen(GetTextForeground(), 0, wxSOLID));
657
658 yUnderscore--;
659
660 DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
661 }
662
663 // return bounding rect if requested
664 if ( rectBounding )
665 {
666 *rectBounding = wxRect(x, y - heightText, widthText, heightText);
667 }
668
669 CalcBoundingBox(x0, y0);
670 CalcBoundingBox(x0 + width0, y0 + height);
671}
12bdd77c
JS
672
673/*
77ffb593 674Notes for wxWidgets DrawEllipticArcRot(...)
12bdd77c
JS
675
676wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
677It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
678which are also new.
679
680All methods are generic, so they can be implemented in wxDCBase.
681DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
682methods like (WinCE) wxDC::DoDrawArc(...).
683
684CalculateEllipticPoints(...) fills a given list of wxPoints with some points
2aaa050c 685of an elliptic arc. The algorithm is pixel-based: In every row (in flat
12bdd77c
JS
686parts) or every column (in steep parts) only one pixel is calculated.
687Trigonometric calculation (sin, cos, tan, atan) is only done if the
2aaa050c 688starting angle is not equal to the ending angle. The calculation of the
12bdd77c
JS
689pixels is done using simple arithmetic only and should perform not too
690bad even on devices without floating point processor. I didn't test this yet.
691
692Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
2aaa050c 693For instance: an ellipse rotated 180 degrees is drawn
12bdd77c
JS
694slightly different from the original.
695
2aaa050c
VZ
696The points are then moved to an array and used to draw a polyline and/or polygon
697(with center added, the pie).
12bdd77c
JS
698The result looks quite similar to the native ellipse, only e few pixels differ.
699
700The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
701slower as DrawEllipse(...), which calls the native API.
702An rotated ellipse outside the clipping region takes nearly the same time,
703while an native ellipse outside takes nearly no time to draw.
704
2aaa050c 705If you draw an arc with this new method, you will see the starting and ending angles
12bdd77c
JS
706are calculated properly.
707If you use DrawEllipticArc(...), you will see they are only correct for circles
708and not properly calculated for ellipses.
709
710Peter Lenhard
711p.lenhard@t-online.de
712*/
713
714#ifdef __WXWINCE__
2aaa050c
VZ
715void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
716 wxCoord w, wxCoord h,
12bdd77c
JS
717 double sa, double ea, double angle )
718{
719 wxList list;
720
721 CalculateEllipticPoints( &list, x, y, w, h, sa, ea );
722 Rotate( &list, angle, wxPoint( x+w/2, y+h/2 ) );
723
724 // Add center (for polygon/pie)
725 list.Append( (wxObject*) new wxPoint( x+w/2, y+h/2 ) );
726
727 // copy list into array and delete list elements
728 int n = list.Number();
729 wxPoint *points = new wxPoint[n];
730 int i = 0;
2aaa050c 731 wxNode* node = 0;
12bdd77c
JS
732 for ( node = list.First(); node; node = node->Next(), i++ )
733 {
734 wxPoint *point = (wxPoint *)node->Data();
735 points[i].x = point->x;
736 points[i].y = point->y;
737 delete point;
738 }
739
740 // first draw the pie without pen, if necessary
63b9e659 741 if( GetBrush() != *wxTRANSPARENT_BRUSH )
12bdd77c
JS
742 {
743 wxPen tempPen( GetPen() );
744 SetPen( *wxTRANSPARENT_PEN );
745 DoDrawPolygon( n, points, 0, 0 );
746 SetPen( tempPen );
747 }
748
749 // then draw the arc without brush, if necessary
63b9e659 750 if( GetPen() != *wxTRANSPARENT_PEN )
12bdd77c
JS
751 {
752 // without center
753 DoDrawLines( n-1, points, 0, 0 );
754 }
755
756 delete [] points;
757
758} // DrawEllipticArcRot
759
760void wxDCBase::Rotate( wxList* points, double angle, wxPoint center )
761{
762 if( angle != 0.0 )
763 {
e0d31471 764 double pi(M_PI);
12bdd77c
JS
765 double dSinA = -sin(angle*2.0*pi/360.0);
766 double dCosA = cos(angle*2.0*pi/360.0);
767 for ( wxNode* node = points->First(); node; node = node->Next() )
768 {
769 wxPoint* point = (wxPoint*)node->Data();
2aaa050c 770
12bdd77c
JS
771 // transform coordinates, if necessary
772 if( center.x ) point->x -= center.x;
773 if( center.y ) point->y -= center.y;
774
775 // calculate rotation, rounding simply by implicit cast to integer
776 int xTemp = point->x * dCosA - point->y * dSinA;
777 point->y = point->x * dSinA + point->y * dCosA;
778 point->x = xTemp;
779
780 // back transform coordinates, if necessary
781 if( center.x ) point->x += center.x;
782 if( center.y ) point->y += center.y;
783 }
784 }
785}
786
2aaa050c
VZ
787void wxDCBase::CalculateEllipticPoints( wxList* points,
788 wxCoord xStart, wxCoord yStart,
789 wxCoord w, wxCoord h,
12bdd77c
JS
790 double sa, double ea )
791{
e0d31471 792 double pi = M_PI;
12bdd77c
JS
793 double sar = 0;
794 double ear = 0;
795 int xsa = 0;
796 int ysa = 0;
797 int xea = 0;
798 int yea = 0;
799 int sq = 0;
800 int eq = 0;
801 bool bUseAngles = false;
802 if( w<0 ) w = -w;
803 if( h<0 ) h = -h;
804 // half-axes
805 wxCoord a = w/2;
806 wxCoord b = h/2;
807 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
808 int decrX = 0;
2aaa050c 809 if( 2*a == w ) decrX = 1;
12bdd77c 810 int decrY = 0;
2aaa050c 811 if( 2*b == h ) decrY = 1;
12bdd77c
JS
812 // center
813 wxCoord xCenter = xStart + a;
814 wxCoord yCenter = yStart + b;
815 // calculate data for start and end, if necessary
816 if( sa != ea )
817 {
818 bUseAngles = true;
819 // normalisation of angles
820 while( sa<0 ) sa += 360;
821 while( ea<0 ) ea += 360;
822 while( sa>=360 ) sa -= 360;
823 while( ea>=360 ) ea -= 360;
824 // calculate quadrant numbers
825 if( sa > 270 ) sq = 3;
826 else if( sa > 180 ) sq = 2;
827 else if( sa > 90 ) sq = 1;
828 if( ea > 270 ) eq = 3;
829 else if( ea > 180 ) eq = 2;
830 else if( ea > 90 ) eq = 1;
831 sar = sa * pi / 180.0;
832 ear = ea * pi / 180.0;
833 // correct angle circle -> ellipse
834 sar = atan( -a/(double)b * tan( sar ) );
2aaa050c 835 if ( sq == 1 || sq == 2 ) sar += pi;
12bdd77c
JS
836 ear = atan( -a/(double)b * tan( ear ) );
837 if ( eq == 1 || eq == 2 ) ear += pi;
838 // coordinates of points
839 xsa = xCenter + a * cos( sar );
840 if( sq == 0 || sq == 3 ) xsa -= decrX;
841 ysa = yCenter + b * sin( sar );
842 if( sq == 2 || sq == 3 ) ysa -= decrY;
843 xea = xCenter + a * cos( ear );
844 if( eq == 0 || eq == 3 ) xea -= decrX;
845 yea = yCenter + b * sin( ear );
846 if( eq == 2 || eq == 3 ) yea -= decrY;
847 } // if iUseAngles
848 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
849 double c1 = b * b;
850 double c2 = 2.0 / w;
851 c2 *= c2;
852 c2 *= c1;
853 wxCoord x = 0;
854 wxCoord y = b;
855 long x2 = 1;
856 long y2 = y*y;
857 long y2_old = 0;
858 long y_old = 0;
859 // Lists for quadrant 1 to 4
860 wxList pointsarray[4];
861 // Calculate points for first quadrant and set in all quadrants
862 for( x = 0; x <= a; ++x )
863 {
864 x2 = x2+x+x-1;
865 y2_old = y2;
866 y_old = y;
867 bool bNewPoint = false;
868 while( y2 > c1 - c2 * x2 && y > 0 )
869 {
870 bNewPoint = true;
871 y2 = y2-y-y+1;
872 --y;
873 }
2aaa050c 874 // old y now to big: set point with old y, old x
12bdd77c
JS
875 if( bNewPoint && x>1)
876 {
877 int x1 = x - 1;
878 // remove points on the same line
879 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter - y_old ) );
880 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - x1, yCenter - y_old ) );
881 pointsarray[2].Insert( (wxObject*) new wxPoint( xCenter - x1, yCenter + y_old - decrY ) );
882 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter + y_old - decrY ) );
883 } // set point
884 } // calculate point
2aaa050c 885
12bdd77c
JS
886 // Starting and/or ending points for the quadrants, first quadrant gets both.
887 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
888 pointsarray[0].Append( (wxObject*) new wxPoint( xCenter, yCenter - b ) );
889 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - a, yCenter ) );
890 pointsarray[2].Append( (wxObject*) new wxPoint( xCenter, yCenter + b - decrY ) );
891 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
892
893 // copy quadrants in original list
894 if( bUseAngles )
895 {
896 // Copy the right part of the points in the lists
897 // and delete the wxPoints, because they do not leave this method.
898 points->Append( (wxObject*) new wxPoint( xsa, ysa ) );
899 int q = sq;
900 bool bStarted = false;
901 bool bReady = false;
902 bool bForceTurn = ( sq == eq && sa > ea );
903 while( !bReady )
904 {
905 for( wxNode *node = pointsarray[q].First(); node; node = node->Next() )
906 {
907 // once: go to starting point in start quadrant
908 if( !bStarted &&
2aaa050c
VZ
909 (
910 ( (wxPoint*) node->Data() )->x < xsa+1 && q <= 1
911 ||
12bdd77c
JS
912 ( (wxPoint*) node->Data() )->x > xsa-1 && q >= 2
913 )
2aaa050c 914 )
12bdd77c
JS
915 {
916 bStarted = true;
917 }
918
919 // copy point, if not at ending point
920 if( bStarted )
921 {
922 if( q != eq || bForceTurn
923 ||
2aaa050c
VZ
924 ( (wxPoint*) node->Data() )->x > xea+1 && q <= 1
925 ||
12bdd77c
JS
926 ( (wxPoint*) node->Data() )->x < xea-1 && q >= 2
927 )
928 {
929 // copy point
930 wxPoint* pPoint = new wxPoint( *((wxPoint*) node->Data() ) );
931 points->Append( (wxObject*) pPoint );
932 }
933 else if( q == eq && !bForceTurn || ( (wxPoint*) node->Data() )->x == xea)
934 {
2aaa050c 935 bReady = true;
12bdd77c
JS
936 }
937 }
938 } // for node
939 ++q;
940 if( q > 3 ) q = 0;
941 bForceTurn = false;
942 bStarted = true;
943 } // while not bReady
944 points->Append( (wxObject*) new wxPoint( xea, yea ) );
945
946 // delete points
947 for( q = 0; q < 4; ++q )
948 {
949 for( wxNode *node = pointsarray[q].First(); node; node = node->Next() )
950 {
951 wxPoint *p = (wxPoint *)node->Data();
952 delete p;
953 }
2aaa050c 954 }
12bdd77c
JS
955 }
956 else
957 {
958 // copy whole ellipse, wxPoints will be deleted outside
959 for( wxNode *node = pointsarray[0].First(); node; node = node->Next() )
960 {
961 wxObject *p = node->Data();
962 points->Append( p );
963 }
964 for( node = pointsarray[1].First(); node; node = node->Next() )
965 {
966 wxObject *p = node->Data();
967 points->Append( p );
968 }
969 for( node = pointsarray[2].First(); node; node = node->Next() )
970 {
971 wxObject *p = node->Data();
972 points->Append( p );
973 }
974 for( node = pointsarray[3].First(); node; node = node->Next() )
975 {
976 wxObject *p = node->Data();
977 points->Append( p );
978 }
979 } // not iUseAngles
980} // CalculateEllipticPoints
981
982#endif
983