1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dcbase.cpp
3 // Purpose: generic methods of the wxDC Class
4 // Author: Vadim Zeitlin
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
33 // bool wxDCBase::sm_cacheing = false;
35 IMPLEMENT_ABSTRACT_CLASS(wxDCBase
, wxObject
)
37 // ============================================================================
39 // ============================================================================
41 #if WXWIN_COMPATIBILITY_2_6
42 void wxDCBase::BeginDrawing()
46 void wxDCBase::EndDrawing()
49 #endif // WXWIN_COMPATIBILITY_2_6
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 void wxDCBase::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
56 wxCoord width
, wxCoord height
)
58 wxCHECK_RET( Ok(), wxT("invalid window dc") );
60 wxCoord x2
= x1
+ width
,
63 // this is to yield width of 3 for width == height == 10
64 SetPen(wxPen(GetTextForeground(), (width
+ height
+ 1) / 7, wxSOLID
));
66 // we're drawing a scaled version of wx/generic/tick.xpm here
67 wxCoord x3
= x1
+ (4*width
) / 10, // x of the tick bottom
68 y3
= y1
+ height
/ 2; // y of the left tick branch
69 DoDrawLine(x1
, y3
, x3
, y2
);
70 DoDrawLine(x3
, y2
, x2
, y1
);
72 CalcBoundingBox(x1
, y1
);
73 CalcBoundingBox(x2
, y2
);
76 // ----------------------------------------------------------------------------
78 // ----------------------------------------------------------------------------
80 void wxDCBase::DrawLines(const wxList
*list
, wxCoord xoffset
, wxCoord yoffset
)
82 int n
= list
->GetCount();
83 wxPoint
*points
= new wxPoint
[n
];
86 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
88 wxPoint
*point
= (wxPoint
*)node
->GetData();
89 points
[i
].x
= point
->x
;
90 points
[i
].y
= point
->y
;
93 DoDrawLines(n
, points
, xoffset
, yoffset
);
99 void wxDCBase::DrawPolygon(const wxList
*list
,
100 wxCoord xoffset
, wxCoord yoffset
,
103 int n
= list
->GetCount();
104 wxPoint
*points
= new wxPoint
[n
];
107 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
109 wxPoint
*point
= (wxPoint
*)node
->GetData();
110 points
[i
].x
= point
->x
;
111 points
[i
].y
= point
->y
;
114 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
120 wxDCBase::DoDrawPolyPolygon(int n
,
123 wxCoord xoffset
, wxCoord yoffset
,
128 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
136 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
141 pts
= new wxPoint
[j
+n
-1];
142 for (i
= 0; i
< j
; i
++)
144 for (i
= 2; i
<= n
; i
++)
146 lastOfs
-= count
[n
-i
];
147 pts
[j
++] = pts
[lastOfs
];
151 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
152 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
154 for (i
= j
= 0; i
< n
; i
++)
156 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
162 // ----------------------------------------------------------------------------
164 // ----------------------------------------------------------------------------
168 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
169 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
173 wxPoint
*point1
= new wxPoint
;
174 point1
->x
= x1
; point1
->y
= y1
;
175 point_list
.Append((wxObject
*)point1
);
177 wxPoint
*point2
= new wxPoint
;
178 point2
->x
= x2
; point2
->y
= y2
;
179 point_list
.Append((wxObject
*)point2
);
181 wxPoint
*point3
= new wxPoint
;
182 point3
->x
= x3
; point3
->y
= y3
;
183 point_list
.Append((wxObject
*)point3
);
185 DrawSpline(&point_list
);
187 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
189 wxPoint
*p
= (wxPoint
*)node
->GetData();
194 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
197 for (int i
=0; i
< n
; i
++)
199 list
.Append((wxObject
*)&points
[i
]);
205 // ----------------------------------- spline code ----------------------------------------
207 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
208 double a3
, double b3
, double a4
, double b4
);
209 void wx_clear_stack();
210 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
211 double *y3
, double *x4
, double *y4
);
212 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
213 double x4
, double y4
);
214 static bool wx_spline_add_point(double x
, double y
);
215 static void wx_spline_draw_point_array(wxDCBase
*dc
);
217 wxList wx_spline_point_list
;
219 #define half(z1, z2) ((z1+z2)/2.0)
222 /* iterative version */
224 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
227 register double xmid
, ymid
;
228 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
231 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
233 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
234 xmid
= (double)half(x2
, x3
);
235 ymid
= (double)half(y2
, y3
);
236 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
237 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
238 wx_spline_add_point( x1
, y1
);
239 wx_spline_add_point( xmid
, ymid
);
241 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
242 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
243 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
244 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
249 /* utilities used by spline drawing routines */
251 typedef struct wx_spline_stack_struct
{
252 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
255 #define SPLINE_STACK_DEPTH 20
256 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
257 static Stack
*wx_stack_top
;
258 static int wx_stack_count
;
260 void wx_clear_stack()
262 wx_stack_top
= wx_spline_stack
;
266 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
268 wx_stack_top
->x1
= x1
;
269 wx_stack_top
->y1
= y1
;
270 wx_stack_top
->x2
= x2
;
271 wx_stack_top
->y2
= y2
;
272 wx_stack_top
->x3
= x3
;
273 wx_stack_top
->y3
= y3
;
274 wx_stack_top
->x4
= x4
;
275 wx_stack_top
->y4
= y4
;
280 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
281 double *x3
, double *y3
, double *x4
, double *y4
)
283 if (wx_stack_count
== 0)
287 *x1
= wx_stack_top
->x1
;
288 *y1
= wx_stack_top
->y1
;
289 *x2
= wx_stack_top
->x2
;
290 *y2
= wx_stack_top
->y2
;
291 *x3
= wx_stack_top
->x3
;
292 *y3
= wx_stack_top
->y3
;
293 *x4
= wx_stack_top
->x4
;
294 *y4
= wx_stack_top
->y4
;
298 static bool wx_spline_add_point(double x
, double y
)
300 wxPoint
*point
= new wxPoint
;
303 wx_spline_point_list
.Append((wxObject
*)point
);
307 static void wx_spline_draw_point_array(wxDCBase
*dc
)
309 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
310 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
313 wxPoint
*point
= (wxPoint
*)node
->GetData();
315 wx_spline_point_list
.Erase(node
);
316 node
= wx_spline_point_list
.GetFirst();
320 void wxDCBase::DoDrawSpline( wxList
*points
)
322 wxCHECK_RET( Ok(), wxT("invalid window dc") );
325 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
326 double x1
, y1
, x2
, y2
;
328 wxList::compatibility_iterator node
= points
->GetFirst();
329 if (node
== wxList::compatibility_iterator())
333 p
= (wxPoint
*)node
->GetData();
338 node
= node
->GetNext();
339 p
= (wxPoint
*)node
->GetData();
343 cx1
= (double)((x1
+ x2
) / 2);
344 cy1
= (double)((y1
+ y2
) / 2);
345 cx2
= (double)((cx1
+ x2
) / 2);
346 cy2
= (double)((cy1
+ y2
) / 2);
348 wx_spline_add_point(x1
, y1
);
350 while ((node
= node
->GetNext())
356 p
= (wxPoint
*)node
->GetData();
361 cx4
= (double)(x1
+ x2
) / 2;
362 cy4
= (double)(y1
+ y2
) / 2;
363 cx3
= (double)(x1
+ cx4
) / 2;
364 cy3
= (double)(y1
+ cy4
) / 2;
366 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
370 cx2
= (double)(cx1
+ x2
) / 2;
371 cy2
= (double)(cy1
+ y2
) / 2;
374 wx_spline_add_point( cx1
, cy1
);
375 wx_spline_add_point( x2
, y2
);
377 wx_spline_draw_point_array( this );
380 #endif // wxUSE_SPLINES
382 // ----------------------------------------------------------------------------
383 // Partial Text Extents
384 // ----------------------------------------------------------------------------
387 // Each element of the widths array will be the width of the string up to and
388 // including the corresponding character in text. This is the generic
389 // implementation, the port-specific classes should do this with native APIs
390 // if available and if faster. Note: pango_layout_index_to_pos is much slower
391 // than calling GetTextExtent!!
398 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
399 ~FontWidthCache() { delete []m_widths
; }
404 m_widths
= new int[FWC_SIZE
];
406 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
414 static FontWidthCache s_fontWidthCache
;
416 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
420 const size_t len
= text
.length();
424 // reset the cache if font or horizontal scale have changed
425 if ( !s_fontWidthCache
.m_widths
||
426 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
427 (s_fontWidthCache
.m_font
!= GetFont()) )
429 s_fontWidthCache
.Reset();
430 s_fontWidthCache
.m_font
= GetFont();
431 s_fontWidthCache
.m_scaleX
= m_scaleX
;
434 // Calculate the position of each character based on the widths of
435 // the previous characters
437 for ( size_t i
= 0; i
< len
; i
++ )
439 const wxChar c
= text
[i
];
440 unsigned int c_int
= (unsigned int)c
;
442 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
444 w
= s_fontWidthCache
.m_widths
[c_int
];
448 GetTextExtent(c
, &w
, &h
);
449 if (c_int
< FWC_SIZE
)
450 s_fontWidthCache
.m_widths
[c_int
] = w
;
454 widths
[i
] = totalWidth
;
461 // ----------------------------------------------------------------------------
462 // enhanced text drawing
463 // ----------------------------------------------------------------------------
465 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
471 wxCoord widthTextMax
= 0, widthLine
,
472 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
475 for ( const wxChar
*pc
= text
; ; pc
++ )
477 if ( *pc
== _T('\n') || *pc
== _T('\0') )
479 if ( curLine
.empty() )
481 // we can't use GetTextExtent - it will return 0 for both width
482 // and height and an empty line should count in height
485 // assume that this line has the same height as the previous
487 if ( !heightLineDefault
)
488 heightLineDefault
= heightLine
;
490 if ( !heightLineDefault
)
492 // but we don't know it yet - choose something reasonable
493 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
497 heightTextTotal
+= heightLineDefault
;
501 GetTextExtent(curLine
, &widthLine
, &heightLine
,
503 if ( widthLine
> widthTextMax
)
504 widthTextMax
= widthLine
;
505 heightTextTotal
+= heightLine
;
508 if ( *pc
== _T('\n') )
527 *y
= heightTextTotal
;
532 void wxDCBase::DrawLabel(const wxString
& text
,
533 const wxBitmap
& bitmap
,
537 wxRect
*rectBounding
)
539 // find the text position
540 wxCoord widthText
, heightText
, heightLine
;
541 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
543 wxCoord width
, height
;
546 width
= widthText
+ bitmap
.GetWidth();
547 height
= bitmap
.GetHeight();
556 if ( alignment
& wxALIGN_RIGHT
)
558 x
= rect
.GetRight() - width
;
560 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
562 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
564 else // alignment & wxALIGN_LEFT
569 if ( alignment
& wxALIGN_BOTTOM
)
571 y
= rect
.GetBottom() - height
;
573 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
575 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
577 else // alignment & wxALIGN_TOP
582 // draw the bitmap first
588 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
590 wxCoord offset
= bitmap
.GetWidth() + 4;
594 y
+= (height
- heightText
) / 2;
597 // we will draw the underscore under the accel char later
598 wxCoord startUnderscore
= 0,
602 // split the string into lines and draw each of them separately
604 for ( const wxChar
*pc
= text
; ; pc
++ )
606 if ( *pc
== _T('\n') || *pc
== _T('\0') )
608 int xRealStart
= x
; // init it here to avoid compielr warnings
610 if ( !curLine
.empty() )
612 // NB: can't test for !(alignment & wxALIGN_LEFT) because
614 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
617 GetTextExtent(curLine
, &widthLine
, NULL
);
619 if ( alignment
& wxALIGN_RIGHT
)
621 xRealStart
+= width
- widthLine
;
623 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
625 xRealStart
+= (width
- widthLine
) / 2;
628 //else: left aligned, nothing to do
630 DrawText(curLine
, xRealStart
, y
);
635 // do we have underscore in this line? we can check yUnderscore
636 // because it is set below to just y + heightLine if we do
637 if ( y
== yUnderscore
)
639 // adjust the horz positions to account for the shift
640 startUnderscore
+= xRealStart
;
641 endUnderscore
+= xRealStart
;
644 if ( *pc
== _T('\0') )
649 else // not end of line
651 if ( pc
- text
.c_str() == indexAccel
)
653 // remeber to draw underscore here
654 GetTextExtent(curLine
, &startUnderscore
, NULL
);
656 GetTextExtent(curLine
, &endUnderscore
, NULL
);
658 yUnderscore
= y
+ heightLine
;
667 // draw the underscore if found
668 if ( startUnderscore
!= endUnderscore
)
670 // it should be of the same colour as text
671 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
675 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
678 // return bounding rect if requested
681 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
684 CalcBoundingBox(x0
, y0
);
685 CalcBoundingBox(x0
+ width0
, y0
+ height
);
689 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
690 const wxColour
& initialColour
,
691 const wxColour
& destColour
,
692 wxDirection nDirection
)
695 wxPen oldPen
= m_pen
;
697 wxUint8 nR1
= destColour
.Red();
698 wxUint8 nG1
= destColour
.Green();
699 wxUint8 nB1
= destColour
.Blue();
700 wxUint8 nR2
= initialColour
.Red();
701 wxUint8 nG2
= initialColour
.Green();
702 wxUint8 nB2
= initialColour
.Blue();
705 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
707 wxInt32 x
= rect
.GetWidth();
708 wxInt32 w
= x
; // width of area to shade
709 wxInt32 xDelta
= w
/256; // height of one shade bend
717 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
719 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
722 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
724 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
727 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
729 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
731 SetPen(wxPen(wxColour(nR
, nG
, nB
), 1, wxSOLID
));
732 if(nDirection
== wxEAST
)
733 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
734 xDelta
, rect
.GetHeight());
735 else //nDirection == wxWEST
736 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
737 xDelta
, rect
.GetHeight());
740 else // nDirection == wxNORTH || nDirection == wxSOUTH
742 wxInt32 y
= rect
.GetHeight();
743 wxInt32 w
= y
; // height of area to shade
744 wxInt32 yDelta
= w
/255; // height of one shade bend
752 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
754 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
757 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
759 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
762 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
764 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
766 SetPen(wxPen(wxColour(nR
, nG
, nB
), 1, wxSOLID
));
767 if(nDirection
== wxNORTH
)
768 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
769 rect
.GetWidth(), yDelta
);
770 else //nDirection == wxSOUTH
771 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
772 rect
.GetWidth(), yDelta
);
779 void wxDCBase::DoGradientFillConcentric(const wxRect
& rect
,
780 const wxColour
& initialColour
,
781 const wxColour
& destColour
,
782 const wxPoint
& circleCenter
)
784 //save the old pen color
785 wxColour oldPenColour
= m_pen
.GetColour();
787 wxUint8 nR1
= destColour
.Red();
788 wxUint8 nG1
= destColour
.Green();
789 wxUint8 nB1
= destColour
.Blue();
790 wxUint8 nR2
= initialColour
.Red();
791 wxUint8 nG2
= initialColour
.Green();
792 wxUint8 nB2
= initialColour
.Blue();
797 wxInt32 cx
= rect
.GetWidth() / 2;
798 wxInt32 cy
= rect
.GetHeight() / 2;
806 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
807 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
809 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
811 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
813 //get color difference
814 wxInt32 nGradient
= ((nRadius
-
816 pow((double)(x
- cx
- nCircleOffX
), 2) +
817 pow((double)(y
- cy
- nCircleOffY
), 2)
825 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
826 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
827 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
830 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
831 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
834 //return old pen color
835 m_pen
.SetColour(oldPenColour
);
839 Notes for wxWidgets DrawEllipticArcRot(...)
841 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
842 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
845 All methods are generic, so they can be implemented in wxDCBase.
846 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
847 methods like (WinCE) wxDC::DoDrawArc(...).
849 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
850 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
851 parts) or every column (in steep parts) only one pixel is calculated.
852 Trigonometric calculation (sin, cos, tan, atan) is only done if the
853 starting angle is not equal to the ending angle. The calculation of the
854 pixels is done using simple arithmetic only and should perform not too
855 bad even on devices without floating point processor. I didn't test this yet.
857 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
858 For instance: an ellipse rotated 180 degrees is drawn
859 slightly different from the original.
861 The points are then moved to an array and used to draw a polyline and/or polygon
862 (with center added, the pie).
863 The result looks quite similar to the native ellipse, only e few pixels differ.
865 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
866 slower as DrawEllipse(...), which calls the native API.
867 An rotated ellipse outside the clipping region takes nearly the same time,
868 while an native ellipse outside takes nearly no time to draw.
870 If you draw an arc with this new method, you will see the starting and ending angles
871 are calculated properly.
872 If you use DrawEllipticArc(...), you will see they are only correct for circles
873 and not properly calculated for ellipses.
876 p.lenhard@t-online.de
880 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
881 wxCoord w
, wxCoord h
,
882 double sa
, double ea
, double angle
)
886 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
887 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
889 // Add center (for polygon/pie)
890 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
892 // copy list into array and delete list elements
893 int n
= list
.GetCount();
894 wxPoint
*points
= new wxPoint
[n
];
897 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
899 wxPoint
*point
= (wxPoint
*)node
->GetData();
900 points
[i
].x
= point
->x
;
901 points
[i
].y
= point
->y
;
905 // first draw the pie without pen, if necessary
906 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
908 wxPen
tempPen( GetPen() );
909 SetPen( *wxTRANSPARENT_PEN
);
910 DoDrawPolygon( n
, points
, 0, 0 );
914 // then draw the arc without brush, if necessary
915 if( GetPen() != *wxTRANSPARENT_PEN
)
918 DoDrawLines( n
-1, points
, 0, 0 );
923 } // DrawEllipticArcRot
925 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
930 double dSinA
= -sin(angle
*2.0*pi
/360.0);
931 double dCosA
= cos(angle
*2.0*pi
/360.0);
932 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
934 wxPoint
* point
= (wxPoint
*)node
->GetData();
936 // transform coordinates, if necessary
937 if( center
.x
) point
->x
-= center
.x
;
938 if( center
.y
) point
->y
-= center
.y
;
940 // calculate rotation, rounding simply by implicit cast to integer
941 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
942 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
945 // back transform coordinates, if necessary
946 if( center
.x
) point
->x
+= center
.x
;
947 if( center
.y
) point
->y
+= center
.y
;
952 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
953 wxCoord xStart
, wxCoord yStart
,
954 wxCoord w
, wxCoord h
,
955 double sa
, double ea
)
966 bool bUseAngles
= false;
972 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
974 if( 2*a
== w
) decrX
= 1;
976 if( 2*b
== h
) decrY
= 1;
978 wxCoord xCenter
= xStart
+ a
;
979 wxCoord yCenter
= yStart
+ b
;
980 // calculate data for start and end, if necessary
984 // normalisation of angles
985 while( sa
<0 ) sa
+= 360;
986 while( ea
<0 ) ea
+= 360;
987 while( sa
>=360 ) sa
-= 360;
988 while( ea
>=360 ) ea
-= 360;
989 // calculate quadrant numbers
990 if( sa
> 270 ) sq
= 3;
991 else if( sa
> 180 ) sq
= 2;
992 else if( sa
> 90 ) sq
= 1;
993 if( ea
> 270 ) eq
= 3;
994 else if( ea
> 180 ) eq
= 2;
995 else if( ea
> 90 ) eq
= 1;
996 sar
= sa
* pi
/ 180.0;
997 ear
= ea
* pi
/ 180.0;
998 // correct angle circle -> ellipse
999 sar
= atan( -a
/(double)b
* tan( sar
) );
1000 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1001 ear
= atan( -a
/(double)b
* tan( ear
) );
1002 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1003 // coordinates of points
1004 xsa
= xCenter
+ a
* cos( sar
);
1005 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1006 ysa
= yCenter
+ b
* sin( sar
);
1007 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1008 xea
= xCenter
+ a
* cos( ear
);
1009 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1010 yea
= yCenter
+ b
* sin( ear
);
1011 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1013 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1015 double c2
= 2.0 / w
;
1024 // Lists for quadrant 1 to 4
1025 wxList pointsarray
[4];
1026 // Calculate points for first quadrant and set in all quadrants
1027 for( x
= 0; x
<= a
; ++x
)
1032 bool bNewPoint
= false;
1033 while( y2
> c1
- c2
* x2
&& y
> 0 )
1039 // old y now to big: set point with old y, old x
1040 if( bNewPoint
&& x
>1)
1043 // remove points on the same line
1044 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1045 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1046 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1047 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1049 } // calculate point
1051 // Starting and/or ending points for the quadrants, first quadrant gets both.
1052 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1053 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1054 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1055 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1056 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1058 // copy quadrants in original list
1061 // Copy the right part of the points in the lists
1062 // and delete the wxPoints, because they do not leave this method.
1063 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1065 bool bStarted
= false;
1066 bool bReady
= false;
1067 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1070 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1072 // once: go to starting point in start quadrant
1075 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1077 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1084 // copy point, if not at ending point
1087 if( q
!= eq
|| bForceTurn
1089 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1091 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1095 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1096 points
->Append( (wxObject
*) pPoint
);
1098 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1108 } // while not bReady
1109 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1112 for( q
= 0; q
< 4; ++q
)
1114 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1116 wxPoint
*p
= (wxPoint
*)node
->GetData();
1124 // copy whole ellipse, wxPoints will be deleted outside
1125 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1127 wxObject
*p
= node
->GetData();
1128 points
->Append( p
);
1130 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1132 wxObject
*p
= node
->GetData();
1133 points
->Append( p
);
1135 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1137 wxObject
*p
= node
->GetData();
1138 points
->Append( p
);
1140 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1142 wxObject
*p
= node
->GetData();
1143 points
->Append( p
);
1146 } // CalculateEllipticPoints