From cebba6c0b9eb74f1a3c4b860c0d35f8d3693e578 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Wed, 8 May 2019 11:17:06 +0200 Subject: [PATCH] Cairo GAL: fix incorrect arc position in mirror mode. the previous fix (c56e540e5dbdbd6536256ecb5a3ed0d10e63a0df) did not really work. Fix from master branch. --- common/gal/cairo/cairo_gal.cpp | 74 +++++++++++++++++++--------------- include/gal/cairo/cairo_gal.h | 19 +++++++-- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp index eecd4f503f..48ffd55ff9 100644 --- a/common/gal/cairo/cairo_gal.cpp +++ b/common/gal/cairo/cairo_gal.cpp @@ -117,9 +117,37 @@ const VECTOR2D CAIRO_GAL_BASE::xform( const VECTOR2D& aP ) const double CAIRO_GAL_BASE::angle_xform( const double aAngle ) { - double world_angle = -std::atan2( currentWorld2Screen.xy, currentWorld2Screen.xx ); + // calculate rotation angle due to the rotation transform + // and if flipped on X axis. + double world_rotation = -std::atan2( currentWorld2Screen.xy, currentWorld2Screen.xx ); - return std::fmod( aAngle + world_angle, 2.0 * M_PI ); + // When flipped on X axis, the rotation angle is M_PI - initial angle: + if( IsFlippedX() ) + world_rotation = M_PI - world_rotation; + + return std::fmod( aAngle + world_rotation, 2.0 * M_PI ); +} + + +void CAIRO_GAL_BASE::arc_angles_xform_and_normalize( double& aStartAngle, double& aEndAngle ) +{ + double startAngle = aStartAngle; + double endAngle = aEndAngle; + + // When the view is flipped, the coordinates are flipped by the matrix transform + // However, arc angles need to be "flipped": the flipped angle is M_PI - initial angle. + if( IsFlippedX() ) + { + startAngle = M_PI - startAngle; + endAngle = M_PI - endAngle; + } + + // Normalize arc angles + SWAP( startAngle, >, endAngle ); + + // now rotate arc according to the rotation transform matrix + aStartAngle = angle_xform( startAngle ); + aEndAngle = angle_xform( endAngle ); } @@ -259,21 +287,11 @@ void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, doub { syncLineWidth(); - // When the view is flipped, the coordinates are flipped by the matrix transform - // However, arc angles need a small change: swapping start and end, *without changing* - // the arc orientation. - // TODO: see the changes if the flip is for the Y axis - if( IsFlippedX() ) - { - double delta = aEndAngle - aStartAngle; - aEndAngle = aStartAngle; - aStartAngle -= delta; - } + // calculate start and end arc angles according to the rotation transform matrix + // and normalize: + arc_angles_xform_and_normalize( aStartAngle, aEndAngle ); - SWAP( aStartAngle, >, aEndAngle ); - auto startAngleS = angle_xform( aStartAngle ); - auto endAngleS = angle_xform( aEndAngle ); - auto r = xform( aRadius ); + double r = xform( aRadius ); // N.B. This is backwards. We set this because we want to adjust the center // point that changes both endpoints. In the worst case, this is twice as far. @@ -289,7 +307,7 @@ void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, doub if( isFillEnabled ) cairo_move_to( currentContext, mid.x, mid.y ); - cairo_arc( currentContext, mid.x, mid.y, r, startAngleS, endAngleS ); + cairo_arc( currentContext, mid.x, mid.y, r, aStartAngle, aEndAngle ); if( isFillEnabled ) cairo_close_path( currentContext ); @@ -316,21 +334,13 @@ void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadiu syncLineWidth(); - // When the view is flipped, the coordinates are flipped by the matrix transform - // However, arc angles need a small change: swapping start and end, *without changing* - // the arc orientation. - // TODO: see the changes if the flip is for the Y axis - if( IsFlippedX() ) - { - double delta = aEndAngle - aStartAngle; - aEndAngle = aStartAngle; - aStartAngle -= delta; - } + // calculate start and end arc angles according to the rotation transform matrix + // and normalize: + double startAngleS = aStartAngle; + double endAngleS = aEndAngle; + arc_angles_xform_and_normalize( startAngleS, endAngleS ); - SWAP( aStartAngle, >, aEndAngle ); - auto startAngleS = angle_xform( aStartAngle ); - auto endAngleS = angle_xform( aEndAngle ); - auto r = xform( aRadius ); + double r = xform( aRadius ); // N.B. This is backwards. We set this because we want to adjust the center // point that changes both endpoints. In the worst case, this is twice as far. @@ -339,7 +349,7 @@ void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadiu lineWidthIsOdd = !( static_cast( aRadius ) % 1 ); auto mid = roundp( xform( aCenterPoint ) ); - auto width = xform( aWidth / 2.0 ); + double width = xform( aWidth / 2.0 ); auto startPointS = VECTOR2D( r, 0.0 ).Rotate( startAngleS ); auto endPointS = VECTOR2D( r, 0.0 ).Rotate( endAngleS ); diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h index e40253a796..ad28a2f109 100644 --- a/include/gal/cairo/cairo_gal.h +++ b/include/gal/cairo/cairo_gal.h @@ -219,11 +219,24 @@ public: protected: - const double xform( double x ); - const VECTOR2D xform( double x, double y ); - const VECTOR2D xform( const VECTOR2D& aP ); + // Geometric transforms according to the currentWorld2Screen transform matrix: + const double xform( double x ); // scale + const VECTOR2D xform( double x, double y ); // rotation, scale and offset + const VECTOR2D xform( const VECTOR2D& aP ); // rotation, scale and offset + + /** Transform according to the rotation from currentWorld2Screen transform matrix: + * @param aAngle is the angle in radians to transform + * @return the modified angle + */ const double angle_xform( const double aAngle ); + /** Transform according to the rotation from currentWorld2Screen transform matrix + * for the start angle and the end angle of an arc + * @param aStartAngle is the arc starting point in radians to transform + * @param aEndAngle is the arc ending point in radians to transform + */ + void arc_angles_xform_and_normalize( double& aStartAngle, double& aEndAngle ); + /// @copydoc GAL::BeginDrawing() virtual void beginDrawing() override;