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"
28 #include "wx/dcbuffer.h" // for IMPLEMENT_DYNAMIC_CLASS
34 // bool wxDCBase::sm_cacheing = false;
36 IMPLEMENT_ABSTRACT_CLASS(wxDCBase
, wxObject
)
38 // ============================================================================
40 // ============================================================================
42 IMPLEMENT_DYNAMIC_CLASS(wxBufferedDC
, wxMemoryDC
)
43 IMPLEMENT_ABSTRACT_CLASS(wxBufferedPaintDC
, wxBufferedDC
)
45 #if WXWIN_COMPATIBILITY_2_6
46 void wxDCBase::BeginDrawing()
50 void wxDCBase::EndDrawing()
53 #endif // WXWIN_COMPATIBILITY_2_6
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 void wxDCBase::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
60 wxCoord width
, wxCoord height
)
62 wxCHECK_RET( Ok(), wxT("invalid window dc") );
64 wxCoord x2
= x1
+ width
,
67 // this is to yield width of 3 for width == height == 10
68 SetPen(wxPen(GetTextForeground(), (width
+ height
+ 1) / 7, wxSOLID
));
70 // we're drawing a scaled version of wx/generic/tick.xpm here
71 wxCoord x3
= x1
+ (4*width
) / 10, // x of the tick bottom
72 y3
= y1
+ height
/ 2; // y of the left tick branch
73 DoDrawLine(x1
, y3
, x3
, y2
);
74 DoDrawLine(x3
, y2
, x2
, y1
);
76 CalcBoundingBox(x1
, y1
);
77 CalcBoundingBox(x2
, y2
);
80 // ----------------------------------------------------------------------------
82 // ----------------------------------------------------------------------------
84 void wxDCBase::DrawLines(const wxList
*list
, wxCoord xoffset
, wxCoord yoffset
)
86 int n
= list
->GetCount();
87 wxPoint
*points
= new wxPoint
[n
];
90 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
92 wxPoint
*point
= (wxPoint
*)node
->GetData();
93 points
[i
].x
= point
->x
;
94 points
[i
].y
= point
->y
;
97 DoDrawLines(n
, points
, xoffset
, yoffset
);
103 void wxDCBase::DrawPolygon(const wxList
*list
,
104 wxCoord xoffset
, wxCoord yoffset
,
107 int n
= list
->GetCount();
108 wxPoint
*points
= new wxPoint
[n
];
111 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
113 wxPoint
*point
= (wxPoint
*)node
->GetData();
114 points
[i
].x
= point
->x
;
115 points
[i
].y
= point
->y
;
118 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
124 wxDCBase::DoDrawPolyPolygon(int n
,
127 wxCoord xoffset
, wxCoord yoffset
,
132 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
140 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
145 pts
= new wxPoint
[j
+n
-1];
146 for (i
= 0; i
< j
; i
++)
148 for (i
= 2; i
<= n
; i
++)
150 lastOfs
-= count
[n
-i
];
151 pts
[j
++] = pts
[lastOfs
];
155 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
156 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
158 for (i
= j
= 0; i
< n
; i
++)
160 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
166 // ----------------------------------------------------------------------------
168 // ----------------------------------------------------------------------------
172 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
173 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
177 wxPoint
*point1
= new wxPoint
;
178 point1
->x
= x1
; point1
->y
= y1
;
179 point_list
.Append((wxObject
*)point1
);
181 wxPoint
*point2
= new wxPoint
;
182 point2
->x
= x2
; point2
->y
= y2
;
183 point_list
.Append((wxObject
*)point2
);
185 wxPoint
*point3
= new wxPoint
;
186 point3
->x
= x3
; point3
->y
= y3
;
187 point_list
.Append((wxObject
*)point3
);
189 DrawSpline(&point_list
);
191 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
193 wxPoint
*p
= (wxPoint
*)node
->GetData();
198 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
201 for (int i
=0; i
< n
; i
++)
203 list
.Append((wxObject
*)&points
[i
]);
209 // ----------------------------------- spline code ----------------------------------------
211 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
212 double a3
, double b3
, double a4
, double b4
);
213 void wx_clear_stack();
214 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
215 double *y3
, double *x4
, double *y4
);
216 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
217 double x4
, double y4
);
218 static bool wx_spline_add_point(double x
, double y
);
219 static void wx_spline_draw_point_array(wxDCBase
*dc
);
221 wxList wx_spline_point_list
;
223 #define half(z1, z2) ((z1+z2)/2.0)
226 /* iterative version */
228 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
231 register double xmid
, ymid
;
232 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
235 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
237 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
238 xmid
= (double)half(x2
, x3
);
239 ymid
= (double)half(y2
, y3
);
240 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
241 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
242 wx_spline_add_point( x1
, y1
);
243 wx_spline_add_point( xmid
, ymid
);
245 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
246 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
247 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
248 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
253 /* utilities used by spline drawing routines */
255 typedef struct wx_spline_stack_struct
{
256 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
259 #define SPLINE_STACK_DEPTH 20
260 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
261 static Stack
*wx_stack_top
;
262 static int wx_stack_count
;
264 void wx_clear_stack()
266 wx_stack_top
= wx_spline_stack
;
270 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
272 wx_stack_top
->x1
= x1
;
273 wx_stack_top
->y1
= y1
;
274 wx_stack_top
->x2
= x2
;
275 wx_stack_top
->y2
= y2
;
276 wx_stack_top
->x3
= x3
;
277 wx_stack_top
->y3
= y3
;
278 wx_stack_top
->x4
= x4
;
279 wx_stack_top
->y4
= y4
;
284 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
285 double *x3
, double *y3
, double *x4
, double *y4
)
287 if (wx_stack_count
== 0)
291 *x1
= wx_stack_top
->x1
;
292 *y1
= wx_stack_top
->y1
;
293 *x2
= wx_stack_top
->x2
;
294 *y2
= wx_stack_top
->y2
;
295 *x3
= wx_stack_top
->x3
;
296 *y3
= wx_stack_top
->y3
;
297 *x4
= wx_stack_top
->x4
;
298 *y4
= wx_stack_top
->y4
;
302 static bool wx_spline_add_point(double x
, double y
)
304 wxPoint
*point
= new wxPoint
;
307 wx_spline_point_list
.Append((wxObject
*)point
);
311 static void wx_spline_draw_point_array(wxDCBase
*dc
)
313 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
314 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
317 wxPoint
*point
= (wxPoint
*)node
->GetData();
319 wx_spline_point_list
.Erase(node
);
320 node
= wx_spline_point_list
.GetFirst();
324 void wxDCBase::DoDrawSpline( wxList
*points
)
326 wxCHECK_RET( Ok(), wxT("invalid window dc") );
329 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
330 double x1
, y1
, x2
, y2
;
332 wxList::compatibility_iterator node
= points
->GetFirst();
337 p
= (wxPoint
*)node
->GetData();
342 node
= node
->GetNext();
343 p
= (wxPoint
*)node
->GetData();
347 cx1
= (double)((x1
+ x2
) / 2);
348 cy1
= (double)((y1
+ y2
) / 2);
349 cx2
= (double)((cx1
+ x2
) / 2);
350 cy2
= (double)((cy1
+ y2
) / 2);
352 wx_spline_add_point(x1
, y1
);
354 while ((node
= node
->GetNext())
360 p
= (wxPoint
*)node
->GetData();
365 cx4
= (double)(x1
+ x2
) / 2;
366 cy4
= (double)(y1
+ y2
) / 2;
367 cx3
= (double)(x1
+ cx4
) / 2;
368 cy3
= (double)(y1
+ cy4
) / 2;
370 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
374 cx2
= (double)(cx1
+ x2
) / 2;
375 cy2
= (double)(cy1
+ y2
) / 2;
378 wx_spline_add_point( cx1
, cy1
);
379 wx_spline_add_point( x2
, y2
);
381 wx_spline_draw_point_array( this );
384 #endif // wxUSE_SPLINES
386 // ----------------------------------------------------------------------------
387 // Partial Text Extents
388 // ----------------------------------------------------------------------------
391 // Each element of the widths array will be the width of the string up to and
392 // including the corresponding character in text. This is the generic
393 // implementation, the port-specific classes should do this with native APIs
394 // if available and if faster. Note: pango_layout_index_to_pos is much slower
395 // than calling GetTextExtent!!
402 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
403 ~FontWidthCache() { delete []m_widths
; }
408 m_widths
= new int[FWC_SIZE
];
410 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
418 static FontWidthCache s_fontWidthCache
;
420 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
424 const size_t len
= text
.length();
428 // reset the cache if font or horizontal scale have changed
429 if ( !s_fontWidthCache
.m_widths
||
430 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
431 (s_fontWidthCache
.m_font
!= GetFont()) )
433 s_fontWidthCache
.Reset();
434 s_fontWidthCache
.m_font
= GetFont();
435 s_fontWidthCache
.m_scaleX
= m_scaleX
;
438 // Calculate the position of each character based on the widths of
439 // the previous characters
441 for ( size_t i
= 0; i
< len
; i
++ )
443 const wxChar c
= text
[i
];
444 unsigned int c_int
= (unsigned int)c
;
446 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
448 w
= s_fontWidthCache
.m_widths
[c_int
];
452 GetTextExtent(c
, &w
, &h
);
453 if (c_int
< FWC_SIZE
)
454 s_fontWidthCache
.m_widths
[c_int
] = w
;
458 widths
[i
] = totalWidth
;
465 // ----------------------------------------------------------------------------
466 // enhanced text drawing
467 // ----------------------------------------------------------------------------
469 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
475 wxCoord widthTextMax
= 0, widthLine
,
476 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
479 for ( const wxChar
*pc
= text
; ; pc
++ )
481 if ( *pc
== _T('\n') || *pc
== _T('\0') )
483 if ( curLine
.empty() )
485 // we can't use GetTextExtent - it will return 0 for both width
486 // and height and an empty line should count in height
489 // assume that this line has the same height as the previous
491 if ( !heightLineDefault
)
492 heightLineDefault
= heightLine
;
494 if ( !heightLineDefault
)
496 // but we don't know it yet - choose something reasonable
497 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
501 heightTextTotal
+= heightLineDefault
;
505 GetTextExtent(curLine
, &widthLine
, &heightLine
,
507 if ( widthLine
> widthTextMax
)
508 widthTextMax
= widthLine
;
509 heightTextTotal
+= heightLine
;
512 if ( *pc
== _T('\n') )
531 *y
= heightTextTotal
;
536 void wxDCBase::DrawLabel(const wxString
& text
,
537 const wxBitmap
& bitmap
,
541 wxRect
*rectBounding
)
543 // find the text position
544 wxCoord widthText
, heightText
, heightLine
;
545 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
547 wxCoord width
, height
;
550 width
= widthText
+ bitmap
.GetWidth();
551 height
= bitmap
.GetHeight();
560 if ( alignment
& wxALIGN_RIGHT
)
562 x
= rect
.GetRight() - width
;
564 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
566 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
568 else // alignment & wxALIGN_LEFT
573 if ( alignment
& wxALIGN_BOTTOM
)
575 y
= rect
.GetBottom() - height
;
577 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
579 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
581 else // alignment & wxALIGN_TOP
586 // draw the bitmap first
592 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
594 wxCoord offset
= bitmap
.GetWidth() + 4;
598 y
+= (height
- heightText
) / 2;
601 // we will draw the underscore under the accel char later
602 wxCoord startUnderscore
= 0,
606 // split the string into lines and draw each of them separately
608 for ( const wxChar
*pc
= text
; ; pc
++ )
610 if ( *pc
== _T('\n') || *pc
== _T('\0') )
612 int xRealStart
= x
; // init it here to avoid compielr warnings
614 if ( !curLine
.empty() )
616 // NB: can't test for !(alignment & wxALIGN_LEFT) because
618 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
621 GetTextExtent(curLine
, &widthLine
, NULL
);
623 if ( alignment
& wxALIGN_RIGHT
)
625 xRealStart
+= width
- widthLine
;
627 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
629 xRealStart
+= (width
- widthLine
) / 2;
632 //else: left aligned, nothing to do
634 DrawText(curLine
, xRealStart
, y
);
639 // do we have underscore in this line? we can check yUnderscore
640 // because it is set below to just y + heightLine if we do
641 if ( y
== yUnderscore
)
643 // adjust the horz positions to account for the shift
644 startUnderscore
+= xRealStart
;
645 endUnderscore
+= xRealStart
;
648 if ( *pc
== _T('\0') )
653 else // not end of line
655 if ( pc
- text
.c_str() == indexAccel
)
657 // remeber to draw underscore here
658 GetTextExtent(curLine
, &startUnderscore
, NULL
);
660 GetTextExtent(curLine
, &endUnderscore
, NULL
);
662 yUnderscore
= y
+ heightLine
;
671 // draw the underscore if found
672 if ( startUnderscore
!= endUnderscore
)
674 // it should be of the same colour as text
675 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
679 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
682 // return bounding rect if requested
685 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
688 CalcBoundingBox(x0
, y0
);
689 CalcBoundingBox(x0
+ width0
, y0
+ height
);
693 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
694 const wxColour
& initialColour
,
695 const wxColour
& destColour
,
696 wxDirection nDirection
)
699 wxPen oldPen
= m_pen
;
701 wxUint8 nR1
= destColour
.Red();
702 wxUint8 nG1
= destColour
.Green();
703 wxUint8 nB1
= destColour
.Blue();
704 wxUint8 nR2
= initialColour
.Red();
705 wxUint8 nG2
= initialColour
.Green();
706 wxUint8 nB2
= initialColour
.Blue();
709 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
711 wxInt32 x
= rect
.GetWidth();
712 wxInt32 w
= x
; // width of area to shade
713 wxInt32 xDelta
= w
/256; // height of one shade bend
721 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
723 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
726 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
728 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
731 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
733 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
735 SetPen(wxPen(wxColour(nR
, nG
, nB
), 1, wxSOLID
));
736 if(nDirection
== wxEAST
)
737 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
738 xDelta
, rect
.GetHeight());
739 else //nDirection == wxWEST
740 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
741 xDelta
, rect
.GetHeight());
744 else // nDirection == wxNORTH || nDirection == wxSOUTH
746 wxInt32 y
= rect
.GetHeight();
747 wxInt32 w
= y
; // height of area to shade
748 wxInt32 yDelta
= w
/255; // height of one shade bend
756 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
758 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
761 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
763 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
766 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
768 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
770 SetPen(wxPen(wxColour(nR
, nG
, nB
), 1, wxSOLID
));
771 if(nDirection
== wxNORTH
)
772 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
773 rect
.GetWidth(), yDelta
);
774 else //nDirection == wxSOUTH
775 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
776 rect
.GetWidth(), yDelta
);
783 void wxDCBase::DoGradientFillConcentric(const wxRect
& rect
,
784 const wxColour
& initialColour
,
785 const wxColour
& destColour
,
786 const wxPoint
& circleCenter
)
788 //save the old pen color
789 wxColour oldPenColour
= m_pen
.GetColour();
791 wxUint8 nR1
= destColour
.Red();
792 wxUint8 nG1
= destColour
.Green();
793 wxUint8 nB1
= destColour
.Blue();
794 wxUint8 nR2
= initialColour
.Red();
795 wxUint8 nG2
= initialColour
.Green();
796 wxUint8 nB2
= initialColour
.Blue();
801 wxInt32 cx
= rect
.GetWidth() / 2;
802 wxInt32 cy
= rect
.GetHeight() / 2;
810 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
811 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
813 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
815 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
817 //get color difference
818 wxInt32 nGradient
= ((nRadius
-
820 pow((double)(x
- cx
- nCircleOffX
), 2) +
821 pow((double)(y
- cy
- nCircleOffY
), 2)
829 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
830 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
831 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
834 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
835 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
838 //return old pen color
839 m_pen
.SetColour(oldPenColour
);
843 Notes for wxWidgets DrawEllipticArcRot(...)
845 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
846 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
849 All methods are generic, so they can be implemented in wxDCBase.
850 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
851 methods like (WinCE) wxDC::DoDrawArc(...).
853 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
854 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
855 parts) or every column (in steep parts) only one pixel is calculated.
856 Trigonometric calculation (sin, cos, tan, atan) is only done if the
857 starting angle is not equal to the ending angle. The calculation of the
858 pixels is done using simple arithmetic only and should perform not too
859 bad even on devices without floating point processor. I didn't test this yet.
861 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
862 For instance: an ellipse rotated 180 degrees is drawn
863 slightly different from the original.
865 The points are then moved to an array and used to draw a polyline and/or polygon
866 (with center added, the pie).
867 The result looks quite similar to the native ellipse, only e few pixels differ.
869 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
870 slower as DrawEllipse(...), which calls the native API.
871 An rotated ellipse outside the clipping region takes nearly the same time,
872 while an native ellipse outside takes nearly no time to draw.
874 If you draw an arc with this new method, you will see the starting and ending angles
875 are calculated properly.
876 If you use DrawEllipticArc(...), you will see they are only correct for circles
877 and not properly calculated for ellipses.
880 p.lenhard@t-online.de
884 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
885 wxCoord w
, wxCoord h
,
886 double sa
, double ea
, double angle
)
890 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
891 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
893 // Add center (for polygon/pie)
894 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
896 // copy list into array and delete list elements
897 int n
= list
.GetCount();
898 wxPoint
*points
= new wxPoint
[n
];
901 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
903 wxPoint
*point
= (wxPoint
*)node
->GetData();
904 points
[i
].x
= point
->x
;
905 points
[i
].y
= point
->y
;
909 // first draw the pie without pen, if necessary
910 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
912 wxPen
tempPen( GetPen() );
913 SetPen( *wxTRANSPARENT_PEN
);
914 DoDrawPolygon( n
, points
, 0, 0 );
918 // then draw the arc without brush, if necessary
919 if( GetPen() != *wxTRANSPARENT_PEN
)
922 DoDrawLines( n
-1, points
, 0, 0 );
927 } // DrawEllipticArcRot
929 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
934 double dSinA
= -sin(angle
*2.0*pi
/360.0);
935 double dCosA
= cos(angle
*2.0*pi
/360.0);
936 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
938 wxPoint
* point
= (wxPoint
*)node
->GetData();
940 // transform coordinates, if necessary
941 if( center
.x
) point
->x
-= center
.x
;
942 if( center
.y
) point
->y
-= center
.y
;
944 // calculate rotation, rounding simply by implicit cast to integer
945 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
946 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
949 // back transform coordinates, if necessary
950 if( center
.x
) point
->x
+= center
.x
;
951 if( center
.y
) point
->y
+= center
.y
;
956 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
957 wxCoord xStart
, wxCoord yStart
,
958 wxCoord w
, wxCoord h
,
959 double sa
, double ea
)
970 bool bUseAngles
= false;
976 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
978 if( 2*a
== w
) decrX
= 1;
980 if( 2*b
== h
) decrY
= 1;
982 wxCoord xCenter
= xStart
+ a
;
983 wxCoord yCenter
= yStart
+ b
;
984 // calculate data for start and end, if necessary
988 // normalisation of angles
989 while( sa
<0 ) sa
+= 360;
990 while( ea
<0 ) ea
+= 360;
991 while( sa
>=360 ) sa
-= 360;
992 while( ea
>=360 ) ea
-= 360;
993 // calculate quadrant numbers
994 if( sa
> 270 ) sq
= 3;
995 else if( sa
> 180 ) sq
= 2;
996 else if( sa
> 90 ) sq
= 1;
997 if( ea
> 270 ) eq
= 3;
998 else if( ea
> 180 ) eq
= 2;
999 else if( ea
> 90 ) eq
= 1;
1000 sar
= sa
* pi
/ 180.0;
1001 ear
= ea
* pi
/ 180.0;
1002 // correct angle circle -> ellipse
1003 sar
= atan( -a
/(double)b
* tan( sar
) );
1004 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1005 ear
= atan( -a
/(double)b
* tan( ear
) );
1006 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1007 // coordinates of points
1008 xsa
= xCenter
+ a
* cos( sar
);
1009 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1010 ysa
= yCenter
+ b
* sin( sar
);
1011 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1012 xea
= xCenter
+ a
* cos( ear
);
1013 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1014 yea
= yCenter
+ b
* sin( ear
);
1015 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1017 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1019 double c2
= 2.0 / w
;
1028 // Lists for quadrant 1 to 4
1029 wxList pointsarray
[4];
1030 // Calculate points for first quadrant and set in all quadrants
1031 for( x
= 0; x
<= a
; ++x
)
1036 bool bNewPoint
= false;
1037 while( y2
> c1
- c2
* x2
&& y
> 0 )
1043 // old y now to big: set point with old y, old x
1044 if( bNewPoint
&& x
>1)
1047 // remove points on the same line
1048 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1049 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1050 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1051 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1053 } // calculate point
1055 // Starting and/or ending points for the quadrants, first quadrant gets both.
1056 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1057 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1058 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1059 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1060 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1062 // copy quadrants in original list
1065 // Copy the right part of the points in the lists
1066 // and delete the wxPoints, because they do not leave this method.
1067 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1069 bool bStarted
= false;
1070 bool bReady
= false;
1071 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1074 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1076 // once: go to starting point in start quadrant
1079 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1081 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1088 // copy point, if not at ending point
1091 if( q
!= eq
|| bForceTurn
1093 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1095 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1099 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1100 points
->Append( (wxObject
*) pPoint
);
1102 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1112 } // while not bReady
1113 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1116 for( q
= 0; q
< 4; ++q
)
1118 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1120 wxPoint
*p
= (wxPoint
*)node
->GetData();
1128 // copy whole ellipse, wxPoints will be deleted outside
1129 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1131 wxObject
*p
= node
->GetData();
1132 points
->Append( p
);
1134 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1136 wxObject
*p
= node
->GetData();
1137 points
->Append( p
);
1139 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1141 wxObject
*p
= node
->GetData();
1142 points
->Append( p
);
1144 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1146 wxObject
*p
= node
->GetData();
1147 points
->Append( p
);
1150 } // CalculateEllipticPoints