Fixed adding arc to wxGraphicsPath with GDI+ renderer.

To handle properly all combinations of the start and end angles and to ensure consistency with Cairo renderer behaviour there is necessary:
1. To normalize angle values the same way as it is done in Cairo.
and
2. When end angle equals start angle then actually no arc should be added but current point of the path has to be updated.
3. When difference between end angle and start angle >= 2*pi then in addition to the arc itself also one or more full circles have to be added to the path.

Closes #17558
This commit is contained in:
Artur Wieczorek 2016-06-04 10:25:05 +02:00
parent 9f9d593019
commit 1f49c4bf40

View File

@ -1293,26 +1293,88 @@ void wxGDIPlusPathData::GetCurrentPoint( wxDouble* x, wxDouble* y) const
void wxGDIPlusPathData::AddArc( wxDouble x, wxDouble y, wxDouble r, double startAngle, double endAngle, bool clockwise )
{
double sweepAngle = endAngle - startAngle ;
if( fabs(sweepAngle) >= 2*M_PI)
double angle;
// For the sake of consistency normalize angles the same way
// as it is done in Cairo.
if ( clockwise )
{
sweepAngle = 2 * M_PI;
// If endAngle < startAngle it needs to be progressively
// increased by 2*M_PI until endAngle > startAngle.
if ( endAngle < startAngle )
{
while ( endAngle <= startAngle )
{
endAngle += 2.0*M_PI;
}
}
angle = endAngle - startAngle;
}
else
{
if ( clockwise )
// If endAngle > startAngle it needs to be progressively
// decreased by 2*M_PI until endAngle < startAngle.
if ( endAngle > startAngle )
{
if( sweepAngle < 0 )
sweepAngle += 2 * M_PI;
while ( endAngle >= startAngle )
{
endAngle -= 2.0*M_PI;
}
}
angle = startAngle - endAngle;
}
// Native GraphicsPath.AddArc() does nothing when sweep
// angle equals 0 (even current point is not updated)
// so we have to handle this case on our own.
if ( angle == 0 )
{
wxPoint2DDouble start = wxPoint2DDouble(cos(startAngle) * r, sin(startAngle) * r);
if (m_figureOpened)
{
AddLineToPoint(start.m_x + x, start.m_y + y);
}
else
{
if( sweepAngle > 0 )
sweepAngle -= 2 * M_PI;
MoveToPoint(start.m_x + x, start.m_y + y);
}
}
m_path->AddArc((REAL) (x-r),(REAL) (y-r),(REAL) (2*r),(REAL) (2*r),wxRadToDeg(startAngle),wxRadToDeg(sweepAngle));
return;
}
REAL x0 = (REAL)(x-r);
REAL y0 = (REAL)(y-r);
REAL dim = (REAL)(2*r);
if ( angle >= 2.0*M_PI )
{
// In addition to arc we need to draw full circle(s).
// Remarks:
// 1. Parity of the number of the circles has to be
// preserved because this matters when path would be
// filled with wxODDEVEN_RULE flag set (using
// FillModeAlternate mode) when number of the edges
// is counted.
// 2. With GraphicsPath.AddEllipse() we cannot
// control the start point of the drawn circle
// so we need to construct it from two arcs (halves).
int numCircles = (int)(angle / (2.0*M_PI));
numCircles = (numCircles - 1) % 2 + 1;
for( int i = 0; i < numCircles; i++ )
{
m_path->AddArc(x0, y0, dim, dim,
wxRadToDeg(startAngle), clockwise ? 180 : -180);
m_path->AddArc(x0, y0, dim, dim,
wxRadToDeg(startAngle+M_PI), clockwise ? 180 : -180);
}
// We need to reduce the angle to [0..2*M_PI) range
angle = fmod(angle, 2.0*M_PI);
}
m_path->AddArc(x0, y0, dim, dim, wxRadToDeg(startAngle),
wxRadToDeg(clockwise ? angle : -angle));
// After calling AddArc() the native current point will be updated and can be used.
m_logCurrentPointSet = false;
}