int D_PAD::boundingRadius() const { int x, y; int radius; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: radius = m_Size.x / 2; break; case PAD_SHAPE_OVAL: radius = std::max( m_Size.x, m_Size.y ) / 2; break; case PAD_SHAPE_RECT: radius = 1 + KiROUND( EuclideanNorm( m_Size ) / 2 ); break; case PAD_SHAPE_TRAPEZOID: x = m_Size.x + std::abs( m_DeltaSize.y ); // Remember: m_DeltaSize.y is the m_Size.x change y = m_Size.y + std::abs( m_DeltaSize.x ); // Remember: m_DeltaSize.x is the m_Size.y change radius = 1 + KiROUND( hypot( x, y ) / 2 ); break; case PAD_SHAPE_ROUNDRECT: radius = GetRoundRectCornerRadius(); x = m_Size.x >> 1; y = m_Size.y >> 1; radius += 1 + KiROUND( EuclideanNorm( wxSize( x - radius, y - radius ))); break; case PAD_SHAPE_CUSTOM: radius = 0; for( int cnt = 0; cnt < m_customShapeAsPolygon.OutlineCount(); ++cnt ) { const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( cnt ); for( int ii = 0; ii < poly.PointCount(); ++ii ) { int dist = KiROUND( poly.CPoint( ii ).EuclideanNorm() ); radius = std::max( radius, dist ); } } radius += 1; break; default: radius = 0; } return radius; }
int D_PAD::boundingRadius() const { int x, y; int radius; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: radius = m_Size.x / 2; break; case PAD_SHAPE_OVAL: radius = std::max( m_Size.x, m_Size.y ) / 2; break; case PAD_SHAPE_RECT: radius = 1 + KiROUND( EuclideanNorm( m_Size ) / 2 ); break; case PAD_SHAPE_TRAPEZOID: x = m_Size.x + std::abs( m_DeltaSize.y ); // Remember: m_DeltaSize.y is the m_Size.x change y = m_Size.y + std::abs( m_DeltaSize.x ); // Remember: m_DeltaSize.x is the m_Size.y change radius = 1 + KiROUND( hypot( x, y ) / 2 ); break; case PAD_SHAPE_ROUNDRECT: radius = GetRoundRectCornerRadius(); x = m_Size.x >> 1; y = m_Size.y >> 1; radius += 1 + KiROUND( EuclideanNorm( wxSize( x - radius, y - radius ))); break; default: radius = 0; } return radius; }
bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const { EDA_RECT arect = aRect; arect.Normalize(); arect.Inflate( aAccuracy ); wxPoint shapePos = ShapePos(); EDA_RECT shapeRect; int r; EDA_RECT bb = GetBoundingBox(); wxPoint endCenter; int radius; if( !arect.Intersects( bb ) ) return false; // This covers total containment for all test cases if( arect.Contains( bb ) ) return true; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() ); case PAD_SHAPE_RECT: shapeRect.SetOrigin( shapePos ); shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 ); return arect.Intersects( shapeRect, m_Orient ); case PAD_SHAPE_OVAL: // Circlular test if dimensions are equal if( m_Size.x == m_Size.y ) return arect.IntersectsCircle( shapePos, GetBoundingRadius() ); shapeRect.SetOrigin( shapePos ); // Horizontal dimension is greater if( m_Size.x > m_Size.y ) { radius = m_Size.y / 2; shapeRect.Inflate( m_Size.x / 2 - radius, radius ); endCenter = wxPoint( m_Size.x / 2 - radius, 0 ); RotatePoint( &endCenter, m_Orient ); // Test circular ends if( arect.IntersectsCircle( shapePos + endCenter, radius ) || arect.IntersectsCircle( shapePos - endCenter, radius ) ) { return true; } } else { radius = m_Size.x / 2; shapeRect.Inflate( radius, m_Size.y / 2 - radius ); endCenter = wxPoint( 0, m_Size.y / 2 - radius ); RotatePoint( &endCenter, m_Orient ); // Test circular ends if( arect.IntersectsCircle( shapePos + endCenter, radius ) || arect.IntersectsCircle( shapePos - endCenter, radius ) ) { return true; } } // Test rectangular portion between rounded ends if( arect.Intersects( shapeRect, m_Orient ) ) { return true; } break; case PAD_SHAPE_TRAPEZOID: /* Trapezoid intersection tests: * A) Any points of rect inside trapezoid * B) Any points of trapezoid inside rect * C) Any sides of trapezoid cross rect */ { wxPoint poly[4]; BuildPadPolygon( poly, wxSize( 0, 0 ), 0 ); wxPoint corners[4]; corners[0] = wxPoint( arect.GetLeft(), arect.GetTop() ); corners[1] = wxPoint( arect.GetRight(), arect.GetTop() ); corners[2] = wxPoint( arect.GetRight(), arect.GetBottom() ); corners[3] = wxPoint( arect.GetLeft(), arect.GetBottom() ); for( int i=0; i<4; i++ ) { RotatePoint( &poly[i], m_Orient ); poly[i] += shapePos; } for( int ii=0; ii<4; ii++ ) { if( TestPointInsidePolygon( poly, 4, corners[ii] ) ) { return true; } if( arect.Contains( poly[ii] ) ) { return true; } if( arect.Intersects( poly[ii], poly[(ii+1) % 4] ) ) { return true; } } return false; } case PAD_SHAPE_ROUNDRECT: /* RoundRect intersection can be broken up into simple tests: * a) Test intersection of horizontal rect * b) Test intersection of vertical rect * c) Test intersection of each corner */ r = GetRoundRectCornerRadius(); /* Test A - intersection of horizontal rect */ shapeRect.SetSize( 0, 0 ); shapeRect.SetOrigin( shapePos ); shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 - r ); // Short-circuit test for zero width or height if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 && arect.Intersects( shapeRect, m_Orient ) ) { return true; } /* Test B - intersection of vertical rect */ shapeRect.SetSize( 0, 0 ); shapeRect.SetOrigin( shapePos ); shapeRect.Inflate( m_Size.x / 2 - r, m_Size.y / 2 ); // Short-circuit test for zero width or height if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 && arect.Intersects( shapeRect, m_Orient ) ) { return true; } /* Test C - intersection of each corner */ endCenter = wxPoint( m_Size.x / 2 - r, m_Size.y / 2 - r ); RotatePoint( &endCenter, m_Orient ); if( arect.IntersectsCircle( shapePos + endCenter, r ) || arect.IntersectsCircle( shapePos - endCenter, r ) ) { return true; } endCenter = wxPoint( m_Size.x / 2 - r, -m_Size.y / 2 + r ); RotatePoint( &endCenter, m_Orient ); if( arect.IntersectsCircle( shapePos + endCenter, r ) || arect.IntersectsCircle( shapePos - endCenter, r ) ) { return true; } break; default: break; } return false; }
bool D_PAD::HitTest( const wxPoint& aPosition ) const { int dx, dy; wxPoint shape_pos = ShapePos(); wxPoint delta = aPosition - shape_pos; // first test: a test point must be inside a minimum sized bounding circle. int radius = GetBoundingRadius(); if( ( abs( delta.x ) > radius ) || ( abs( delta.y ) > radius ) ) return false; dx = m_Size.x >> 1; // dx also is the radius for rounded pads dy = m_Size.y >> 1; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: if( KiROUND( EuclideanNorm( delta ) ) <= dx ) return true; break; case PAD_SHAPE_TRAPEZOID: { wxPoint poly[4]; BuildPadPolygon( poly, wxSize(0,0), 0 ); RotatePoint( &delta, -m_Orient ); return TestPointInsidePolygon( poly, 4, delta ); } case PAD_SHAPE_OVAL: { RotatePoint( &delta, -m_Orient ); // An oval pad has the same shape as a segment with rounded ends // After rotation, the test point is relative to an horizontal pad int dist; wxPoint offset; if( dy > dx ) // shape is a vertical oval { offset.y = dy - dx; dist = dx; } else //if( dy <= dx ) shape is an horizontal oval { offset.x = dy - dx; dist = dy; } return TestSegmentHit( delta, - offset, offset, dist ); } break; case PAD_SHAPE_RECT: RotatePoint( &delta, -m_Orient ); if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) ) return true; break; case PAD_SHAPE_ROUNDRECT: { // Check for hit in polygon SHAPE_POLY_SET outline; const int segmentToCircleCount = 32; TransformRoundRectToPolygon( outline, wxPoint(0,0), GetSize(), m_Orient, GetRoundRectCornerRadius(), segmentToCircleCount ); const SHAPE_LINE_CHAIN &poly = outline.COutline( 0 ); return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta ); } break; case PAD_SHAPE_CUSTOM: // Check for hit in polygon RotatePoint( &delta, -m_Orient ); if( m_customShapeAsPolygon.OutlineCount() ) { const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( 0 ); return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta ); } break; } return false; }
void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, int aError, bool ignoreLineWidth ) const { wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for pads." ); double angle = m_Orient; int dx = (m_Size.x / 2) + aClearanceValue; int dy = (m_Size.y / 2) + aClearanceValue; wxPoint padShapePos = ShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ switch( GetShape() ) { case PAD_SHAPE_CIRCLE: { TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError ); } break; case PAD_SHAPE_OVAL: // An oval pad has the same shape as a segment with rounded ends { int width; wxPoint shape_offset; if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis { shape_offset.y = dy - dx; width = dx * 2; } else //if( dy <= dx ) { shape_offset.x = dy - dx; width = dy * 2; } RotatePoint( &shape_offset, angle ); wxPoint start = padShapePos - shape_offset; wxPoint end = padShapePos + shape_offset; TransformOvalClearanceToPolygon( aCornerBuffer, start, end, width, aError ); } break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: { wxPoint corners[4]; BuildPadPolygon( corners, wxSize( 0, 0 ), angle ); SHAPE_POLY_SET outline; outline.NewOutline(); for( int ii = 0; ii < 4; ii++ ) { corners[ii] += padShapePos; outline.Append( corners[ii].x, corners[ii].y ); } int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int rounding_radius = KiROUND( aClearanceValue * correction ); outline.Inflate( rounding_radius, numSegs ); aCornerBuffer.Append( outline ); } break; case PAD_SHAPE_CHAMFERED_RECT: case PAD_SHAPE_ROUNDRECT: { SHAPE_POLY_SET outline; int radius = GetRoundRectCornerRadius() + aClearanceValue; int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int clearance = KiROUND( aClearanceValue * correction ); int rounding_radius = GetRoundRectCornerRadius() + clearance; wxSize shapesize( m_Size ); shapesize.x += clearance * 2; shapesize.y += clearance * 2; bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT; TransformRoundChamferedRectToPolygon( outline, padShapePos, shapesize, angle, rounding_radius, doChamfer ? GetChamferRectRatio() : 0.0, doChamfer ? GetChamferPositions() : 0, aError ); aCornerBuffer.Append( outline ); } break; case PAD_SHAPE_CUSTOM: { int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int clearance = KiROUND( aClearanceValue * correction ); SHAPE_POLY_SET outline; // Will contain the corners in board coordinates outline.Append( m_customShapeAsPolygon ); CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() ); outline.Simplify( SHAPE_POLY_SET::PM_FAST ); outline.Inflate( clearance, numSegs ); outline.Fracture( SHAPE_POLY_SET::PM_FAST ); aCornerBuffer.Append( outline ); } break; } }