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"
30 // bool wxDCBase::sm_cacheing = false;
32 IMPLEMENT_ABSTRACT_CLASS(wxDCBase
, wxObject
)
34 // ============================================================================
36 // ============================================================================
38 #if WXWIN_COMPATIBILITY_2_6
39 void wxDCBase::BeginDrawing()
43 void wxDCBase::EndDrawing()
46 #endif // WXWIN_COMPATIBILITY_2_6
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 void wxDCBase::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
53 wxCoord width
, wxCoord height
)
55 wxCHECK_RET( Ok(), wxT("invalid window dc") );
57 wxCoord x2
= x1
+ width
,
60 // this is to yield width of 3 for width == height == 10
61 SetPen(wxPen(GetTextForeground(), (width
+ height
+ 1) / 7, wxSOLID
));
63 // we're drawing a scaled version of wx/generic/tick.xpm here
64 wxCoord x3
= x1
+ (4*width
) / 10, // x of the tick bottom
65 y3
= y1
+ height
/ 2; // y of the left tick branch
66 DoDrawLine(x1
, y3
, x3
, y2
);
67 DoDrawLine(x3
, y2
, x2
, y1
);
69 CalcBoundingBox(x1
, y1
);
70 CalcBoundingBox(x2
, y2
);
73 // ----------------------------------------------------------------------------
75 // ----------------------------------------------------------------------------
77 void wxDCBase::DrawLines(const wxList
*list
, wxCoord xoffset
, wxCoord yoffset
)
79 int n
= list
->GetCount();
80 wxPoint
*points
= new wxPoint
[n
];
83 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
85 wxPoint
*point
= (wxPoint
*)node
->GetData();
86 points
[i
].x
= point
->x
;
87 points
[i
].y
= point
->y
;
90 DoDrawLines(n
, points
, xoffset
, yoffset
);
96 void wxDCBase::DrawPolygon(const wxList
*list
,
97 wxCoord xoffset
, wxCoord yoffset
,
100 int n
= list
->GetCount();
101 wxPoint
*points
= new wxPoint
[n
];
104 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
106 wxPoint
*point
= (wxPoint
*)node
->GetData();
107 points
[i
].x
= point
->x
;
108 points
[i
].y
= point
->y
;
111 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
117 wxDCBase::DoDrawPolyPolygon(int n
,
120 wxCoord xoffset
, wxCoord yoffset
,
125 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
133 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
138 pts
= new wxPoint
[j
+n
-1];
139 for (i
= 0; i
< j
; i
++)
141 for (i
= 2; i
<= n
; i
++)
143 lastOfs
-= count
[n
-i
];
144 pts
[j
++] = pts
[lastOfs
];
148 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
149 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
151 for (i
= j
= 0; i
< n
; i
++)
153 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
159 // ----------------------------------------------------------------------------
161 // ----------------------------------------------------------------------------
165 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
166 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
170 wxPoint
*point1
= new wxPoint
;
171 point1
->x
= x1
; point1
->y
= y1
;
172 point_list
.Append((wxObject
*)point1
);
174 wxPoint
*point2
= new wxPoint
;
175 point2
->x
= x2
; point2
->y
= y2
;
176 point_list
.Append((wxObject
*)point2
);
178 wxPoint
*point3
= new wxPoint
;
179 point3
->x
= x3
; point3
->y
= y3
;
180 point_list
.Append((wxObject
*)point3
);
182 DrawSpline(&point_list
);
184 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
186 wxPoint
*p
= (wxPoint
*)node
->GetData();
191 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
194 for (int i
=0; i
< n
; i
++)
196 list
.Append((wxObject
*)&points
[i
]);
202 // ----------------------------------- spline code ----------------------------------------
204 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
205 double a3
, double b3
, double a4
, double b4
);
206 void wx_clear_stack();
207 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
208 double *y3
, double *x4
, double *y4
);
209 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
210 double x4
, double y4
);
211 static bool wx_spline_add_point(double x
, double y
);
212 static void wx_spline_draw_point_array(wxDCBase
*dc
);
214 wxList wx_spline_point_list
;
216 #define half(z1, z2) ((z1+z2)/2.0)
219 /* iterative version */
221 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
224 register double xmid
, ymid
;
225 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
228 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
230 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
231 xmid
= (double)half(x2
, x3
);
232 ymid
= (double)half(y2
, y3
);
233 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
234 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
235 wx_spline_add_point( x1
, y1
);
236 wx_spline_add_point( xmid
, ymid
);
238 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
239 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
240 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
241 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
246 /* utilities used by spline drawing routines */
248 typedef struct wx_spline_stack_struct
{
249 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
252 #define SPLINE_STACK_DEPTH 20
253 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
254 static Stack
*wx_stack_top
;
255 static int wx_stack_count
;
257 void wx_clear_stack()
259 wx_stack_top
= wx_spline_stack
;
263 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
265 wx_stack_top
->x1
= x1
;
266 wx_stack_top
->y1
= y1
;
267 wx_stack_top
->x2
= x2
;
268 wx_stack_top
->y2
= y2
;
269 wx_stack_top
->x3
= x3
;
270 wx_stack_top
->y3
= y3
;
271 wx_stack_top
->x4
= x4
;
272 wx_stack_top
->y4
= y4
;
277 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
278 double *x3
, double *y3
, double *x4
, double *y4
)
280 if (wx_stack_count
== 0)
284 *x1
= wx_stack_top
->x1
;
285 *y1
= wx_stack_top
->y1
;
286 *x2
= wx_stack_top
->x2
;
287 *y2
= wx_stack_top
->y2
;
288 *x3
= wx_stack_top
->x3
;
289 *y3
= wx_stack_top
->y3
;
290 *x4
= wx_stack_top
->x4
;
291 *y4
= wx_stack_top
->y4
;
295 static bool wx_spline_add_point(double x
, double y
)
297 wxPoint
*point
= new wxPoint
;
300 wx_spline_point_list
.Append((wxObject
*)point
);
304 static void wx_spline_draw_point_array(wxDCBase
*dc
)
306 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
307 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
310 wxPoint
*point
= (wxPoint
*)node
->GetData();
312 wx_spline_point_list
.Erase(node
);
313 node
= wx_spline_point_list
.GetFirst();
317 void wxDCBase::DoDrawSpline( wxList
*points
)
319 wxCHECK_RET( Ok(), wxT("invalid window dc") );
322 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
323 double x1
, y1
, x2
, y2
;
325 wxList::compatibility_iterator node
= points
->GetFirst();
326 if (node
== wxList::compatibility_iterator())
330 p
= (wxPoint
*)node
->GetData();
335 node
= node
->GetNext();
336 p
= (wxPoint
*)node
->GetData();
340 cx1
= (double)((x1
+ x2
) / 2);
341 cy1
= (double)((y1
+ y2
) / 2);
342 cx2
= (double)((cx1
+ x2
) / 2);
343 cy2
= (double)((cy1
+ y2
) / 2);
345 wx_spline_add_point(x1
, y1
);
347 while ((node
= node
->GetNext())
353 p
= (wxPoint
*)node
->GetData();
358 cx4
= (double)(x1
+ x2
) / 2;
359 cy4
= (double)(y1
+ y2
) / 2;
360 cx3
= (double)(x1
+ cx4
) / 2;
361 cy3
= (double)(y1
+ cy4
) / 2;
363 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
367 cx2
= (double)(cx1
+ x2
) / 2;
368 cy2
= (double)(cy1
+ y2
) / 2;
371 wx_spline_add_point( cx1
, cy1
);
372 wx_spline_add_point( x2
, y2
);
374 wx_spline_draw_point_array( this );
377 #endif // wxUSE_SPLINES
379 // ----------------------------------------------------------------------------
380 // Partial Text Extents
381 // ----------------------------------------------------------------------------
384 // Each element of the widths array will be the width of the string up to and
385 // including the corresponding character in text. This is the generic
386 // implementation, the port-specific classes should do this with native APIs
387 // if available and if faster. Note: pango_layout_index_to_pos is much slower
388 // than calling GetTextExtent!!
395 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
396 ~FontWidthCache() { delete []m_widths
; }
401 m_widths
= new int[FWC_SIZE
];
403 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
411 static FontWidthCache s_fontWidthCache
;
413 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
417 const size_t len
= text
.Length();
421 // reset the cache if font or horizontal scale have changed
422 if ( !s_fontWidthCache
.m_widths
||
423 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
424 (s_fontWidthCache
.m_font
!= GetFont()) )
426 s_fontWidthCache
.Reset();
427 s_fontWidthCache
.m_font
= GetFont();
428 s_fontWidthCache
.m_scaleX
= m_scaleX
;
431 // Calculate the position of each character based on the widths of
432 // the previous characters
434 for ( size_t i
= 0; i
< len
; i
++ )
436 const wxChar c
= text
[i
];
437 unsigned int c_int
= (unsigned int)c
;
439 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
441 w
= s_fontWidthCache
.m_widths
[c_int
];
445 GetTextExtent(c
, &w
, &h
);
446 if (c_int
< FWC_SIZE
)
447 s_fontWidthCache
.m_widths
[c_int
] = w
;
451 widths
[i
] = totalWidth
;
458 // ----------------------------------------------------------------------------
459 // enhanced text drawing
460 // ----------------------------------------------------------------------------
462 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
468 wxCoord widthTextMax
= 0, widthLine
,
469 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
472 for ( const wxChar
*pc
= text
; ; pc
++ )
474 if ( *pc
== _T('\n') || *pc
== _T('\0') )
476 if ( curLine
.empty() )
478 // we can't use GetTextExtent - it will return 0 for both width
479 // and height and an empty line should count in height
482 // assume that this line has the same height as the previous
484 if ( !heightLineDefault
)
485 heightLineDefault
= heightLine
;
487 if ( !heightLineDefault
)
489 // but we don't know it yet - choose something reasonable
490 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
494 heightTextTotal
+= heightLineDefault
;
498 GetTextExtent(curLine
, &widthLine
, &heightLine
,
500 if ( widthLine
> widthTextMax
)
501 widthTextMax
= widthLine
;
502 heightTextTotal
+= heightLine
;
505 if ( *pc
== _T('\n') )
524 *y
= heightTextTotal
;
529 void wxDCBase::DrawLabel(const wxString
& text
,
530 const wxBitmap
& bitmap
,
534 wxRect
*rectBounding
)
536 // find the text position
537 wxCoord widthText
, heightText
, heightLine
;
538 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
540 wxCoord width
, height
;
543 width
= widthText
+ bitmap
.GetWidth();
544 height
= bitmap
.GetHeight();
553 if ( alignment
& wxALIGN_RIGHT
)
555 x
= rect
.GetRight() - width
;
557 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
559 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
561 else // alignment & wxALIGN_LEFT
566 if ( alignment
& wxALIGN_BOTTOM
)
568 y
= rect
.GetBottom() - height
;
570 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
572 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
574 else // alignment & wxALIGN_TOP
579 // draw the bitmap first
585 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
587 wxCoord offset
= bitmap
.GetWidth() + 4;
591 y
+= (height
- heightText
) / 2;
594 // we will draw the underscore under the accel char later
595 wxCoord startUnderscore
= 0,
599 // split the string into lines and draw each of them separately
601 for ( const wxChar
*pc
= text
; ; pc
++ )
603 if ( *pc
== _T('\n') || *pc
== _T('\0') )
605 int xRealStart
= x
; // init it here to avoid compielr warnings
607 if ( !curLine
.empty() )
609 // NB: can't test for !(alignment & wxALIGN_LEFT) because
611 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
614 GetTextExtent(curLine
, &widthLine
, NULL
);
616 if ( alignment
& wxALIGN_RIGHT
)
618 xRealStart
+= width
- widthLine
;
620 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
622 xRealStart
+= (width
- widthLine
) / 2;
625 //else: left aligned, nothing to do
627 DrawText(curLine
, xRealStart
, y
);
632 // do we have underscore in this line? we can check yUnderscore
633 // because it is set below to just y + heightLine if we do
634 if ( y
== yUnderscore
)
636 // adjust the horz positions to account for the shift
637 startUnderscore
+= xRealStart
;
638 endUnderscore
+= xRealStart
;
641 if ( *pc
== _T('\0') )
646 else // not end of line
648 if ( pc
- text
.c_str() == indexAccel
)
650 // remeber to draw underscore here
651 GetTextExtent(curLine
, &startUnderscore
, NULL
);
653 GetTextExtent(curLine
, &endUnderscore
, NULL
);
655 yUnderscore
= y
+ heightLine
;
664 // draw the underscore if found
665 if ( startUnderscore
!= endUnderscore
)
667 // it should be of the same colour as text
668 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
672 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
675 // return bounding rect if requested
678 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
681 CalcBoundingBox(x0
, y0
);
682 CalcBoundingBox(x0
+ width0
, y0
+ height
);
686 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
687 const wxColour
& initialColour
,
688 const wxColour
& destColour
,
689 wxDirection nDirection
)
692 wxPen oldPen
= m_pen
;
694 wxUint8 nR1
= destColour
.Red();
695 wxUint8 nG1
= destColour
.Green();
696 wxUint8 nB1
= destColour
.Blue();
697 wxUint8 nR2
= initialColour
.Red();
698 wxUint8 nG2
= initialColour
.Green();
699 wxUint8 nB2
= initialColour
.Blue();
702 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
704 wxInt32 x
= rect
.GetWidth();
705 wxInt32 w
= x
; // width of area to shade
706 wxInt32 xDelta
= w
/256; // height of one shade bend
714 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
716 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
719 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
721 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
724 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
726 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
728 SetPen(wxPen(wxColour(nR
, nG
, nB
), 1, wxSOLID
));
729 if(nDirection
== wxEAST
)
730 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
731 xDelta
, rect
.GetHeight());
732 else //nDirection == wxWEST
733 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
734 xDelta
, rect
.GetHeight());
737 else // nDirection == wxNORTH || nDirection == wxSOUTH
739 wxInt32 y
= rect
.GetHeight();
740 wxInt32 w
= y
; // height of area to shade
741 wxInt32 yDelta
= w
/255; // height of one shade bend
749 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
751 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
754 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
756 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
759 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
761 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
763 SetPen(wxPen(wxColour(nR
, nG
, nB
), 1, wxSOLID
));
764 if(nDirection
== wxNORTH
)
765 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
766 rect
.GetWidth(), yDelta
);
767 else //nDirection == wxSOUTH
768 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
769 rect
.GetWidth(), yDelta
);
776 void wxDCBase::GradientFillConcentric(const wxRect
& rect
,
777 const wxColour
& initialColour
,
778 const wxColour
& destColour
,
779 const wxPoint
& circleCenter
)
781 //save the old pen color
782 wxColour oldPenColour
= m_pen
.GetColour();
784 wxUint8 nR1
= destColour
.Red();
785 wxUint8 nG1
= destColour
.Green();
786 wxUint8 nB1
= destColour
.Blue();
787 wxUint8 nR2
= initialColour
.Red();
788 wxUint8 nG2
= initialColour
.Green();
789 wxUint8 nB2
= initialColour
.Blue();
794 wxInt32 cx
= rect
.GetWidth() / 2;
795 wxInt32 cy
= rect
.GetHeight() / 2;
803 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
804 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
806 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
808 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
810 //get color difference
811 wxInt32 nGradient
= ((nRadius
-
813 pow((double)(x
- cx
- nCircleOffX
), 2) +
814 pow((double)(y
- cy
- nCircleOffY
), 2)
822 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
823 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
824 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
827 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
828 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
831 //return old pen color
832 m_pen
.SetColour(oldPenColour
);
836 Notes for wxWidgets DrawEllipticArcRot(...)
838 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
839 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
842 All methods are generic, so they can be implemented in wxDCBase.
843 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
844 methods like (WinCE) wxDC::DoDrawArc(...).
846 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
847 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
848 parts) or every column (in steep parts) only one pixel is calculated.
849 Trigonometric calculation (sin, cos, tan, atan) is only done if the
850 starting angle is not equal to the ending angle. The calculation of the
851 pixels is done using simple arithmetic only and should perform not too
852 bad even on devices without floating point processor. I didn't test this yet.
854 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
855 For instance: an ellipse rotated 180 degrees is drawn
856 slightly different from the original.
858 The points are then moved to an array and used to draw a polyline and/or polygon
859 (with center added, the pie).
860 The result looks quite similar to the native ellipse, only e few pixels differ.
862 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
863 slower as DrawEllipse(...), which calls the native API.
864 An rotated ellipse outside the clipping region takes nearly the same time,
865 while an native ellipse outside takes nearly no time to draw.
867 If you draw an arc with this new method, you will see the starting and ending angles
868 are calculated properly.
869 If you use DrawEllipticArc(...), you will see they are only correct for circles
870 and not properly calculated for ellipses.
873 p.lenhard@t-online.de
877 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
878 wxCoord w
, wxCoord h
,
879 double sa
, double ea
, double angle
)
883 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
884 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
886 // Add center (for polygon/pie)
887 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
889 // copy list into array and delete list elements
890 int n
= list
.GetCount();
891 wxPoint
*points
= new wxPoint
[n
];
894 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
896 wxPoint
*point
= (wxPoint
*)node
->GetData();
897 points
[i
].x
= point
->x
;
898 points
[i
].y
= point
->y
;
902 // first draw the pie without pen, if necessary
903 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
905 wxPen
tempPen( GetPen() );
906 SetPen( *wxTRANSPARENT_PEN
);
907 DoDrawPolygon( n
, points
, 0, 0 );
911 // then draw the arc without brush, if necessary
912 if( GetPen() != *wxTRANSPARENT_PEN
)
915 DoDrawLines( n
-1, points
, 0, 0 );
920 } // DrawEllipticArcRot
922 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
927 double dSinA
= -sin(angle
*2.0*pi
/360.0);
928 double dCosA
= cos(angle
*2.0*pi
/360.0);
929 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
931 wxPoint
* point
= (wxPoint
*)node
->GetData();
933 // transform coordinates, if necessary
934 if( center
.x
) point
->x
-= center
.x
;
935 if( center
.y
) point
->y
-= center
.y
;
937 // calculate rotation, rounding simply by implicit cast to integer
938 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
939 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
942 // back transform coordinates, if necessary
943 if( center
.x
) point
->x
+= center
.x
;
944 if( center
.y
) point
->y
+= center
.y
;
949 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
950 wxCoord xStart
, wxCoord yStart
,
951 wxCoord w
, wxCoord h
,
952 double sa
, double ea
)
963 bool bUseAngles
= false;
969 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
971 if( 2*a
== w
) decrX
= 1;
973 if( 2*b
== h
) decrY
= 1;
975 wxCoord xCenter
= xStart
+ a
;
976 wxCoord yCenter
= yStart
+ b
;
977 // calculate data for start and end, if necessary
981 // normalisation of angles
982 while( sa
<0 ) sa
+= 360;
983 while( ea
<0 ) ea
+= 360;
984 while( sa
>=360 ) sa
-= 360;
985 while( ea
>=360 ) ea
-= 360;
986 // calculate quadrant numbers
987 if( sa
> 270 ) sq
= 3;
988 else if( sa
> 180 ) sq
= 2;
989 else if( sa
> 90 ) sq
= 1;
990 if( ea
> 270 ) eq
= 3;
991 else if( ea
> 180 ) eq
= 2;
992 else if( ea
> 90 ) eq
= 1;
993 sar
= sa
* pi
/ 180.0;
994 ear
= ea
* pi
/ 180.0;
995 // correct angle circle -> ellipse
996 sar
= atan( -a
/(double)b
* tan( sar
) );
997 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
998 ear
= atan( -a
/(double)b
* tan( ear
) );
999 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1000 // coordinates of points
1001 xsa
= xCenter
+ a
* cos( sar
);
1002 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1003 ysa
= yCenter
+ b
* sin( sar
);
1004 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1005 xea
= xCenter
+ a
* cos( ear
);
1006 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1007 yea
= yCenter
+ b
* sin( ear
);
1008 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1010 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1012 double c2
= 2.0 / w
;
1021 // Lists for quadrant 1 to 4
1022 wxList pointsarray
[4];
1023 // Calculate points for first quadrant and set in all quadrants
1024 for( x
= 0; x
<= a
; ++x
)
1029 bool bNewPoint
= false;
1030 while( y2
> c1
- c2
* x2
&& y
> 0 )
1036 // old y now to big: set point with old y, old x
1037 if( bNewPoint
&& x
>1)
1040 // remove points on the same line
1041 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1042 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1043 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1044 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1046 } // calculate point
1048 // Starting and/or ending points for the quadrants, first quadrant gets both.
1049 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1050 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1051 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1052 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1053 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1055 // copy quadrants in original list
1058 // Copy the right part of the points in the lists
1059 // and delete the wxPoints, because they do not leave this method.
1060 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1062 bool bStarted
= false;
1063 bool bReady
= false;
1064 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1067 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1069 // once: go to starting point in start quadrant
1072 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1074 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1081 // copy point, if not at ending point
1084 if( q
!= eq
|| bForceTurn
1086 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1088 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1092 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1093 points
->Append( (wxObject
*) pPoint
);
1095 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1105 } // while not bReady
1106 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1109 for( q
= 0; q
< 4; ++q
)
1111 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1113 wxPoint
*p
= (wxPoint
*)node
->GetData();
1121 // copy whole ellipse, wxPoints will be deleted outside
1122 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1124 wxObject
*p
= node
->GetData();
1125 points
->Append( p
);
1127 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1129 wxObject
*p
= node
->GetData();
1130 points
->Append( p
);
1132 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1134 wxObject
*p
= node
->GetData();
1135 points
->Append( p
);
1137 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1139 wxObject
*p
= node
->GetData();
1140 points
->Append( p
);
1143 } // CalculateEllipticPoints