Patch up arc hit-testing and printing for 6.0

This could use another look when we're not so near release.  We
really shouldn't need to special case eeWinding vs. not eeWinding.

Fixes https://gitlab.com/kicad/code/kicad/issues/9491
This commit is contained in:
Jeff Young 2021-10-30 15:08:44 +01:00
parent 08700e3522
commit 5f8e0ef1e0
4 changed files with 55 additions and 42 deletions

View File

@ -37,12 +37,13 @@
#include <plotters/plotter.h> #include <plotters/plotter.h>
EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) : EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill, bool eeWinding ) :
m_endsSwapped( false ), m_endsSwapped( false ),
m_shape( aType ), m_shape( aType ),
m_width( aLineWidth ), m_width( aLineWidth ),
m_fill( aFill ), m_fill( aFill ),
m_editState( 0 ) m_editState( 0 ),
m_eeWinding( eeWinding )
{ {
} }
@ -691,16 +692,19 @@ bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const
double endAngle; double endAngle;
CalcArcAngles( startAngle, endAngle ); CalcArcAngles( startAngle, endAngle );
if( m_eeWinding && NormalizeAngleDegrees( startAngle - endAngle, -180.0, 180.0 ) > 0 )
std::swap( startAngle, endAngle );
double relPosAngle = 180.0 / M_PI * atan2( relPos.y, relPos.x ); double relPosAngle = 180.0 / M_PI * atan2( relPos.y, relPos.x );
if( relPosAngle >= startAngle && relPosAngle <= endAngle ) startAngle = NormalizeAngleDegrees( startAngle, 0.0, 360.0 );
return true; endAngle = NormalizeAngleDegrees( endAngle, 0.0, 360.0 );
relPosAngle = NormalizeAngleDegrees( relPosAngle, 0.0, 360.0 ); relPosAngle = NormalizeAngleDegrees( relPosAngle, 0.0, 360.0 );
if( relPosAngle >= startAngle && relPosAngle <= endAngle ) if( endAngle > startAngle )
return true; return relPosAngle >= startAngle && relPosAngle <= endAngle;
else
return relPosAngle >= startAngle || relPosAngle <= endAngle;
} }
return false; return false;
@ -974,36 +978,52 @@ std::vector<wxPoint> EDA_SHAPE::GetRectCorners() const
void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
{ {
// Do not include the center, which is not necessarily wxPoint start = m_start;
// inside the BB of a arc with a small angle wxPoint end = m_end;
aBBox.SetOrigin( m_start ); double t1, t2;
aBBox.Merge( m_end );
CalcArcAngles( t1, t2 );
if( m_eeWinding && NormalizeAngleDegrees( t1 - t2, -180.0, 180.0 ) > 0 )
std::swap( start, end );
// Do not include the center, which is not necessarily inside the BB of an arc with a small
// included angle
aBBox.SetOrigin( start );
aBBox.Merge( end );
// Determine the starting quarter // Determine the starting quarter
// 0 right-bottom // 0 right-bottom
// 1 left-bottom // 1 left-bottom
// 2 left-top // 2 left-top
// 3 right-top // 3 right-top
unsigned int quarter = 0; // assume right-bottom unsigned int quarter;
if( m_start.x < m_arcCenter.x ) if( start.x < m_arcCenter.x )
{ {
if( m_start.y <= m_arcCenter.y ) if( start.y <= m_arcCenter.y )
quarter = 2; quarter = 2;
else // ( m_start.y > m_arcCenter.y ) else
quarter = 1; quarter = 1;
} }
else if( m_start.x >= m_arcCenter.x ) else if( start.x == m_arcCenter.x )
{ {
if( m_start.y < m_arcCenter.y ) if( start.y < m_arcCenter.y )
quarter = 3; quarter = 3;
else if( m_start.x == m_arcCenter.x ) else
quarter = 1; quarter = 1;
} }
else
{
if( start.y < m_arcCenter.y )
quarter = 3;
else
quarter = 0;
}
int radius = GetRadius(); int radius = GetRadius();
VECTOR2I startRadial = GetStart() - getCenter(); VECTOR2I startRadial = start - m_arcCenter;
VECTOR2I endRadial = GetEnd() - getCenter(); VECTOR2I endRadial = end - m_arcCenter;
double angleStart = ArcTangente( startRadial.y, startRadial.x ); double angleStart = ArcTangente( startRadial.y, startRadial.x );
double arcAngle = RAD2DECIDEG( endRadial.Angle() - startRadial.Angle() ); double arcAngle = RAD2DECIDEG( endRadial.Angle() - startRadial.Angle() );
int angle = (int) NormalizeAnglePos( angleStart ) % 900 + NormalizeAnglePos( arcAngle ); int angle = (int) NormalizeAnglePos( angleStart ) % 900 + NormalizeAnglePos( arcAngle );
@ -1012,21 +1032,10 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
{ {
switch( quarter ) switch( quarter )
{ {
case 0: case 0: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y + radius ) ); break; // down
aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y + radius ) ); case 1: aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y ) ); break; // left
break; // down case 2: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y - radius ) ); break; // up
case 3: aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) ); break; // right
case 1:
aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y ) );
break; // left
case 2:
aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y - radius ) );
break; // up
case 3:
aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) );
break; // right
} }
++quarter %= 4; ++quarter %= 4;

View File

@ -36,7 +36,7 @@
LIB_SHAPE::LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape, int aDefaultLineWidth, LIB_SHAPE::LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape, int aDefaultLineWidth,
FILL_T aFillType ) : FILL_T aFillType ) :
LIB_ITEM( LIB_SHAPE_T, aParent ), LIB_ITEM( LIB_SHAPE_T, aParent ),
EDA_SHAPE( aShape, aDefaultLineWidth, aFillType ) EDA_SHAPE( aShape, aDefaultLineWidth, aFillType, true )
{ {
m_editState = 0; m_editState = 0;
} }
@ -255,8 +255,11 @@ void LIB_SHAPE::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset,
CalcArcAngles( t1, t2 ); CalcArcAngles( t1, t2 );
if( t1 > t2 ) if( NormalizeAngle180( t1 - t2 ) > 0 )
aTransform.MapAngles( &t1, &t2 ); {
std::swap( pt1, pt2 );
std::swap( t1, t2 );
}
} }
if( forceNoFill || GetFillType() == FILL_T::NO_FILL ) if( forceNoFill || GetFillType() == FILL_T::NO_FILL )

View File

@ -62,7 +62,7 @@ enum class FILL_T : int
class EDA_SHAPE class EDA_SHAPE
{ {
public: public:
EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ); EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill, bool eeWinding );
// Do not create a copy constructor & operator=. // Do not create a copy constructor & operator=.
// The ones generated by the compiler are adequate. // The ones generated by the compiler are adequate.
@ -308,6 +308,7 @@ protected:
SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape
int m_editState; int m_editState;
bool m_eeWinding; // Awful hack
}; };
#endif // EDA_SHAPE_H #endif // EDA_SHAPE_H

View File

@ -35,14 +35,14 @@
PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T idtype, SHAPE_T shapetype ) : PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T idtype, SHAPE_T shapetype ) :
BOARD_ITEM( aParent, idtype ), BOARD_ITEM( aParent, idtype ),
EDA_SHAPE( shapetype, Millimeter2iu( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL ) EDA_SHAPE( shapetype, Millimeter2iu( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL, false )
{ {
} }
PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, SHAPE_T shapetype ) : PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, SHAPE_T shapetype ) :
BOARD_ITEM( aParent, PCB_SHAPE_T ), BOARD_ITEM( aParent, PCB_SHAPE_T ),
EDA_SHAPE( shapetype, Millimeter2iu( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL ) EDA_SHAPE( shapetype, Millimeter2iu( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL, false )
{ {
} }