From 1f49c4bf408607ebc2043e3e829c5ed801a013ee Mon Sep 17 00:00:00 2001 From: Artur Wieczorek Date: Sat, 4 Jun 2016 10:25:05 +0200 Subject: [PATCH] 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 --- src/msw/graphics.cpp | 84 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/src/msw/graphics.cpp b/src/msw/graphics.cpp index be6c7bce1b..16aee51d0b 100644 --- a/src/msw/graphics.cpp +++ b/src/msw/graphics.cpp @@ -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; }