/* test if distance between a segment is > aMinDist
 * segment start point is assumed in (0,0) and  segment start point in m_segmEnd
 * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
 * and have aSegmentWidth.
 */
bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
{
    wxSize  padHalfsize;        // half the dimension of the pad
    wxPoint startPoint, endPoint;
    int     seuil;
    int     deltay;

    int     segmHalfWidth = aSegmentWidth / 2;

    seuil = segmHalfWidth + aMinDist;
    padHalfsize.x = aPad->GetSize().x >> 1;
    padHalfsize.y = aPad->GetSize().y >> 1;

    if( aPad->GetShape() == PAD_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
    {
        padHalfsize.x += std::abs(aPad->GetDelta().y) / 2;   // Remember: GetDelta().y is the GetSize().x change
        padHalfsize.y += std::abs(aPad->GetDelta().x) / 2;   // Remember: GetDelta().x is the GetSize().y change
    }

    if( aPad->GetShape() == PAD_CIRCLE )
    {
        /* Easy case: just test the distance between segment and pad centre
         * calculate pad coordinates in the X,Y axis with X axis = segment to test
         */
        RotatePoint( &m_padToTestPos, m_segmAngle );
        return checkMarginToCircle( m_padToTestPos, seuil + padHalfsize.x, m_segmLength );
    }

    /* calculate the bounding box of the pad, including the clearance and the segment width
     * if the line from 0 to m_segmEnd does not intersect this bounding box,
     * the clearance is always OK
     * But if intersect, a better analysis of the pad shape must be done.
     */
    m_xcliplo = m_padToTestPos.x - seuil - padHalfsize.x;
    m_ycliplo = m_padToTestPos.y - seuil - padHalfsize.y;
    m_xcliphi = m_padToTestPos.x + seuil + padHalfsize.x;
    m_ycliphi = m_padToTestPos.y + seuil + padHalfsize.y;

    startPoint.x = startPoint.y = 0;
    endPoint     = m_segmEnd;

    double orient = aPad->GetOrientation();

    RotatePoint( &startPoint, m_padToTestPos, -orient );
    RotatePoint( &endPoint, m_padToTestPos, -orient );

    if( checkLine( startPoint, endPoint ) )
        return true;

    /* segment intersects the bounding box. But there is not always a DRC error.
     * A fine analysis of the pad shape must be done.
     */
    switch( aPad->GetShape() )
    {
    default:
        return false;

    case PAD_OVAL:

        /* an oval is a complex shape, but is a rectangle and 2 circles
         * these 3 basic shapes are more easy to test.
         */
        /* We use a vertical oval shape. for horizontal ovals, swap x and y size and rotate the shape*/
        if( padHalfsize.x > padHalfsize.y )
        {
            EXCHG( padHalfsize.x, padHalfsize.y );
            orient = AddAngles( orient, 900 );
        }

        deltay = padHalfsize.y - padHalfsize.x;

        // here: padHalfsize.x = radius, delta = dist centre cercles a centre pad

        // Test the rectangle area between the two circles
        m_xcliplo = m_padToTestPos.x - seuil - padHalfsize.x;
        m_ycliplo = m_padToTestPos.y - segmHalfWidth - deltay;
        m_xcliphi = m_padToTestPos.x + seuil + padHalfsize.x;
        m_ycliphi = m_padToTestPos.y + segmHalfWidth + deltay;

        if( !checkLine( startPoint, endPoint ) )
        {
            return false;
        }

        // test the first circle
        startPoint.x = m_padToTestPos.x;         // startPoint = centre of the upper circle of the oval shape
        startPoint.y = m_padToTestPos.y + deltay;

        // Calculate the actual position of the circle, given the pad orientation:
        RotatePoint( &startPoint, m_padToTestPos, orient );

        // Calculate the actual position of the circle in the new X,Y axis:
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, padHalfsize.x + seuil, m_segmLength ) )
        {
            return false;
        }

        // test the second circle
        startPoint.x = m_padToTestPos.x;         // startPoint = centre of the lower circle of the oval shape
        startPoint.y = m_padToTestPos.y - deltay;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, padHalfsize.x + seuil, m_segmLength ) )
        {
            return false;
        }

        break;

    case PAD_RECT:          /* 2 rectangle + 4 1/4 cercles a tester */
        /* Test du rectangle dimx + seuil, dimy */
        m_xcliplo = m_padToTestPos.x - padHalfsize.x - seuil;
        m_ycliplo = m_padToTestPos.y - padHalfsize.y;
        m_xcliphi = m_padToTestPos.x + padHalfsize.x + seuil;
        m_ycliphi = m_padToTestPos.y + padHalfsize.y;

        if( !checkLine( startPoint, endPoint ) )
            return false;

        /* Test du rectangle dimx , dimy + seuil */
        m_xcliplo = m_padToTestPos.x - padHalfsize.x;
        m_ycliplo = m_padToTestPos.y - padHalfsize.y - seuil;
        m_xcliphi = m_padToTestPos.x + padHalfsize.x;
        m_ycliphi = m_padToTestPos.y + padHalfsize.y + seuil;

        if( !checkLine( startPoint, endPoint ) )
            return false;

        /* test des 4 cercles ( surface d'solation autour des sommets */
        /* test du coin sup. gauche du pad */
        startPoint.x = m_padToTestPos.x - padHalfsize.x;
        startPoint.y = m_padToTestPos.y - padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
            return false;

        /* test du coin sup. droit du pad */
        startPoint.x = m_padToTestPos.x + padHalfsize.x;
        startPoint.y = m_padToTestPos.y - padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
            return false;

        /* test du coin inf. gauche du pad */
        startPoint.x = m_padToTestPos.x - padHalfsize.x;
        startPoint.y = m_padToTestPos.y + padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
            return false;

        /* test du coin inf. droit du pad */
        startPoint.x = m_padToTestPos.x + padHalfsize.x;
        startPoint.y = m_padToTestPos.y + padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
            return false;

        break;

    case PAD_TRAPEZOID:
    {
        wxPoint poly[4];
        aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );

        // Move shape to m_padToTestPos
        for( int ii = 0; ii < 4; ii++ )
        {
            poly[ii] += m_padToTestPos;
            RotatePoint( &poly[ii], m_segmAngle );
        }

        if( !trapezoid2segmentDRC( poly, wxPoint( 0, 0 ), wxPoint(m_segmLength,0), seuil ) )
            return false;
    }
    break;
    }

    return true;
}
/* test if distance between a segment is > aMinDist
 * segment start point is assumed in (0,0) and  segment start point in m_segmEnd
 * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
 * and have aSegmentWidth.
 */
bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
{
    wxSize  padHalfsize;            // half dimension of the pad
    wxPoint startPoint, endPoint;

    int segmHalfWidth = aSegmentWidth / 2;
    int distToLine = segmHalfWidth + aMinDist;

    padHalfsize.x = aPad->GetSize().x >> 1;
    padHalfsize.y = aPad->GetSize().y >> 1;

    if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID )     // The size is bigger, due to GetDelta() extra size
    {
        padHalfsize.x += std::abs(aPad->GetDelta().y) / 2;   // Remember: GetDelta().y is the GetSize().x change
        padHalfsize.y += std::abs(aPad->GetDelta().x) / 2;   // Remember: GetDelta().x is the GetSize().y change
    }

    if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
    {
        /* Easy case: just test the distance between segment and pad centre
         * calculate pad coordinates in the X,Y axis with X axis = segment to test
         */
        RotatePoint( &m_padToTestPos, m_segmAngle );
        return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
    }

    /* calculate the bounding box of the pad, including the clearance and the segment width
     * if the line from 0 to m_segmEnd does not intersect this bounding box,
     * the clearance is always OK
     * But if intersect, a better analysis of the pad shape must be done.
     */
    m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
    m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
    m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
    m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;

    startPoint.x = startPoint.y = 0;
    endPoint     = m_segmEnd;

    double orient = aPad->GetOrientation();

    RotatePoint( &startPoint, m_padToTestPos, -orient );
    RotatePoint( &endPoint, m_padToTestPos, -orient );

    if( checkLine( startPoint, endPoint ) )
        return true;

    /* segment intersects the bounding box. But there is not always a DRC error.
     * A fine analysis of the pad shape must be done.
     */
    switch( aPad->GetShape() )
    {
    default:
        return false;

    case PAD_SHAPE_OVAL:
    {
        /* an oval is a complex shape, but is a rectangle and 2 circles
         * these 3 basic shapes are more easy to test.
         *
         * In calculations we are using a vertical oval shape
         * (i.e. a vertical rounded segment)
         * for horizontal oval shapes, swap x and y size and rotate the shape
         */
        if( padHalfsize.x > padHalfsize.y )
        {
            std::swap( padHalfsize.x, padHalfsize.y );
            orient = AddAngles( orient, 900 );
        }

        // here, padHalfsize.x is the radius of rounded ends.

        int deltay = padHalfsize.y - padHalfsize.x;
        // here: padHalfsize.x = radius,
        // deltay = dist between the centre pad and the centre of a rounded end

        // Test the rectangular area between the two circles (the rounded ends)
        m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
        m_ycliplo = m_padToTestPos.y - deltay;
        m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
        m_ycliphi = m_padToTestPos.y + deltay;

        if( !checkLine( startPoint, endPoint ) )
        {
            return false;
        }

        // test the first circle
        startPoint.x = m_padToTestPos.x;         // startPoint = centre of the upper circle of the oval shape
        startPoint.y = m_padToTestPos.y + deltay;

        // Calculate the actual position of the circle, given the pad orientation:
        RotatePoint( &startPoint, m_padToTestPos, orient );

        // Calculate the actual position of the circle in the new X,Y axis:
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, padHalfsize.x + distToLine, m_segmLength ) )
        {
            return false;
        }

        // test the second circle
        startPoint.x = m_padToTestPos.x;         // startPoint = centre of the lower circle of the oval shape
        startPoint.y = m_padToTestPos.y - deltay;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, padHalfsize.x + distToLine, m_segmLength ) )
        {
            return false;
        }
    }
        break;

    case PAD_SHAPE_RECT:
        // the area to test is a rounded rectangle.
        // this can be done by testing 2 rectangles and 4 circles (the corners)

        // Testing the first rectangle dimx + distToLine, dimy:
        m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
        m_ycliplo = m_padToTestPos.y - padHalfsize.y;
        m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
        m_ycliphi = m_padToTestPos.y + padHalfsize.y;

        if( !checkLine( startPoint, endPoint ) )
            return false;

        // Testing the second rectangle dimx , dimy + distToLine
        m_xcliplo = m_padToTestPos.x - padHalfsize.x;
        m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
        m_xcliphi = m_padToTestPos.x + padHalfsize.x;
        m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;

        if( !checkLine( startPoint, endPoint ) )
            return false;

        // testing the 4 circles which are the clearance area of each corner:

        // testing the left top corner of the rectangle
        startPoint.x = m_padToTestPos.x - padHalfsize.x;
        startPoint.y = m_padToTestPos.y - padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
            return false;

        // testing the right top corner of the rectangle
        startPoint.x = m_padToTestPos.x + padHalfsize.x;
        startPoint.y = m_padToTestPos.y - padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
            return false;

        // testing the left bottom corner of the rectangle
        startPoint.x = m_padToTestPos.x - padHalfsize.x;
        startPoint.y = m_padToTestPos.y + padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
            return false;

        // testing the right bottom corner of the rectangle
        startPoint.x = m_padToTestPos.x + padHalfsize.x;
        startPoint.y = m_padToTestPos.y + padHalfsize.y;
        RotatePoint( &startPoint, m_padToTestPos, orient );
        RotatePoint( &startPoint, m_segmAngle );

        if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
            return false;

        break;

    case PAD_SHAPE_TRAPEZOID:
    {
        wxPoint poly[4];
        aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );

        // Move shape to m_padToTestPos
        for( int ii = 0; ii < 4; ii++ )
        {
            poly[ii] += m_padToTestPos;
            RotatePoint( &poly[ii], m_segmAngle );
        }

        if( !trapezoid2segmentDRC( poly, wxPoint( 0, 0 ), wxPoint(m_segmLength,0), distToLine ) )
            return false;
    }
    break;
    }

    return true;
}