cairo: Prevent arch shaking in pcbnew

Arc pixel adjustment needs to be limited to +/-1 pixel.  But making this
adjustment to nearby points and then using the projection to correct the
center point accumulates errors that are large for angles near 0 and
near 360.  This is a compromise that aligns _most_ arcs and provides
stable generation for all angles.

Fixes: lp:1817226
* https://bugs.launchpad.net/kicad/+bug/1817226
This commit is contained in:
Seth Hillbrand 2019-02-21 19:56:54 -08:00
parent f14cf5cbc7
commit 2305c5458e
1 changed files with 16 additions and 18 deletions

View File

@ -261,22 +261,15 @@ void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, doub
SWAP( aStartAngle, >, aEndAngle ); SWAP( aStartAngle, >, aEndAngle );
auto startAngleS = angle_xform( aStartAngle ); auto startAngleS = angle_xform( aStartAngle );
auto endAngleS = angle_xform( aEndAngle ); auto endAngleS = angle_xform( aEndAngle );
double centerAngle = endAngleS - startAngleS; auto r = xform( aRadius );
auto startPointS = roundp( xform ( aCenterPoint + // N.B. This is backwards. We set this because we want to adjust the center
VECTOR2D( aRadius, 0.0 ).Rotate( startAngleS ) ) ); // point that changes both endpoints. In the worst case, this is twice as far.
auto endPointS = roundp( xform ( aCenterPoint + // We cannot adjust radius or center based on the other because this causes the
VECTOR2D( aRadius, 0.0 ).Rotate( endAngleS ) ) ); // whole arc to change position/size
auto mid = ( startPointS + endPointS ) * 0.5; lineWidthIsOdd = !( static_cast<int>( aRadius ) % 1 );
auto chord = endPointS - startPointS;
double c = chord.EuclideanNorm() / 2.0;
auto d = chord.Rotate( M_PI / 2.0 ).Resize( c );
auto tan_angle = tan( centerAngle / 2.0 );
if( tan_angle != 0.0 ) auto mid = roundp( xform( aCenterPoint ) );
mid += d * ( 1.0 / tan_angle );
auto rS = ( mid - startPointS ).EuclideanNorm();
cairo_set_line_width( currentContext, lineWidthInPixels ); cairo_set_line_width( currentContext, lineWidthInPixels );
cairo_new_sub_path( currentContext ); cairo_new_sub_path( currentContext );
@ -284,7 +277,7 @@ void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, doub
if( isFillEnabled ) if( isFillEnabled )
cairo_move_to( currentContext, mid.x, mid.y ); cairo_move_to( currentContext, mid.x, mid.y );
cairo_arc( currentContext, mid.x, mid.y, rS, startAngleS, endAngleS ); cairo_arc( currentContext, mid.x, mid.y, r, startAngleS, endAngleS );
if( isFillEnabled ) if( isFillEnabled )
cairo_close_path( currentContext ); cairo_close_path( currentContext );
@ -311,13 +304,18 @@ void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadiu
syncLineWidth(); syncLineWidth();
SWAP( aStartAngle, >, aEndAngle ); SWAP( aStartAngle, >, aEndAngle );
auto width = ::roundp( xform( aWidth / 2.0 ) );
auto r = ::roundp( xform( aRadius ) );
auto startAngleS = angle_xform( aStartAngle ); auto startAngleS = angle_xform( aStartAngle );
auto endAngleS = angle_xform( aEndAngle ); auto endAngleS = angle_xform( aEndAngle );
auto 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.
// We cannot adjust radius or center based on the other because this causes the
// whole arc to change position/size
lineWidthIsOdd = !( static_cast<int>( aRadius ) % 1 );
auto mid = roundp( xform( aCenterPoint ) ); auto mid = roundp( xform( aCenterPoint ) );
auto width = xform( aWidth / 2.0 );
auto startPointS = VECTOR2D( r, 0.0 ).Rotate( startAngleS ); auto startPointS = VECTOR2D( r, 0.0 ).Rotate( startAngleS );
auto endPointS = VECTOR2D( r, 0.0 ).Rotate( endAngleS ); auto endPointS = VECTOR2D( r, 0.0 ).Rotate( endAngleS );