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