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