/***
 * @file board_items_to_polygon_shape_transform.cpp
 * @brief function to convert shapes of items ( pads, tracks... ) to polygons
 */

/* Function to convert pads and tranck shapes to polygons
 * Used to fill zones areas
 */
#include <vector>

#include <fctsys.h>
#include <polygons_defs.h>
#include <pcbnew.h>
#include <wxPcbStruct.h>
#include <trigo.h>
#include <class_pad.h>
#include <class_track.h>
#include <class_drawsegment.h>
#include <class_pcb_text.h>
#include <class_zone.h>
#include <convert_basic_shapes_to_polygon.h>

/**
 * Function TransformShapeWithClearanceToPolygon
 * Convert the track shape to a closed polygon
 * Used in filling zones calculations
 * Circles and arcs are approximated by segments
 * @param aCornerBuffer = a buffer to store the polygon
 * @param aClearanceValue = the clearance around the pad
 * @param aCircleToSegmentsCount = the number of segments to approximate a circle
 * @param aCorrectionFactor = the correction to apply to circles radius to keep
 * clearance when the circle is approximated by segment bigger or equal
 * to the real clearance value (usually near from 1.0)
 */
void TEXTE_PCB::TransformShapeWithClearanceToPolygon( std::vector <CPolyPt>& aCornerBuffer,
                                                      int                    aClearanceValue,
                                                      int                    aCircleToSegmentsCount,
                                                      double                 aCorrectionFactor )
{
    if( GetLength() == 0 )
        return;

    CPolyPt  corners[4];    // Buffer of polygon corners

    EDA_RECT rect = GetTextBox( -1 );
    rect.Inflate( aClearanceValue );
    corners[0].x = rect.GetOrigin().x;
    corners[0].y = rect.GetOrigin().y;
    corners[1].y = corners[0].y;
    corners[1].x = rect.GetRight();
    corners[2].x = corners[1].x;
    corners[2].y = rect.GetBottom();
    corners[3].y = corners[2].y;
    corners[3].x = corners[0].x;

    for( int ii = 0; ii < 4; ii++ )
    {
        // Rotate polygon
        RotatePoint( &corners[ii].x, &corners[ii].y, m_Pos.x, m_Pos.y, m_Orient );
        aCornerBuffer.push_back( corners[ii] );
    }

    aCornerBuffer.back().end_contour = true;
}

 /* Function TransformShapeWithClearanceToPolygon
  * Convert the track shape to a closed polygon
  * Used in filling zones calculations
  * Circles (vias) and arcs (ends of tracks) are approximated by segments
  * param aCornerBuffer = a buffer to store the polygon
  * param aClearanceValue = the clearance around the pad
  * param aCircleToSegmentsCount = the number of segments to approximate a circle
  * param aCorrectionFactor = the correction to apply to circles radius to keep
  * param aAddClearance = true to add a clearance area to the polygon
  *                      false to create the outline polygon.
  * clearance when the circle is approximated by segment bigger or equal
  * to the real clearance value (usually near from 1.0)
  */
void ZONE_CONTAINER::TransformShapeWithClearanceToPolygon( std::vector <CPolyPt>& aCornerBuffer,
                                                      int                    aClearanceValue,
                                                      int                    aCircleToSegmentsCount,
                                                      double                 aCorrectionFactor,
                                                      bool                   aAddClearance )
{

    /* Creates the main polygon (i.e. the filled area using only one outline)
     * and reserve a clearance margin around the outlines and holes
     */
    std::vector <CPolyPt> zoneOutines;
    BuildFilledPolysListData( NULL, &zoneOutines );
    int clearance = 0;
    if( aAddClearance )
    {
        GetClearance();
        if( aClearanceValue > clearance )
            clearance = aClearanceValue;
    }

    // Calculate the polygon with clearance and holes
    // holes are linked to the main outline, so only one polygon should be created.
    KI_POLYGON_SET polyset_zone_solid_areas;
    std::vector<KI_POLY_POINT> cornerslist;
    unsigned ic = 0;
    unsigned corners_count = zoneOutines.size();
    while( ic < corners_count )
    {
        cornerslist.clear();
        KI_POLYGON poly;
        {
            for( ; ic < corners_count; ic++ )
            {
                CPolyPt* corner = &zoneOutines[ic];
                cornerslist.push_back( KI_POLY_POINT( corner->x, corner->y ) );
                if( corner->end_contour )
                {
                    ic++;
                    break;
                }
            }

            bpl::set_points( poly, cornerslist.begin(), cornerslist.end() );
            polyset_zone_solid_areas.push_back( poly );
        }
    }

    polyset_zone_solid_areas += clearance;

    // Put the resultng polygon in buffer
    for( unsigned ii = 0; ii < polyset_zone_solid_areas.size(); ii++ )
    {
        KI_POLYGON& poly = polyset_zone_solid_areas[ii];
        CPolyPt   corner( 0, 0, false );

        for( unsigned jj = 0; jj < poly.size(); jj++ )
        {
            KI_POLY_POINT point = *(poly.begin() + jj);
            corner.x = point.x();
            corner.y = point.y();
            corner.end_contour = false;
            aCornerBuffer.push_back( corner );
        }

        corner.end_contour = true;
        aCornerBuffer.pop_back();
        aCornerBuffer.push_back( corner );
    }
}



/**
 * Function TransformShapeWithClearanceToPolygon
 * Convert the track shape to a closed polygon
 * Used in filling zones calculations
 * Circles and arcs are approximated by segments
 * @param aCornerBuffer = a buffer to store the polygon
 * @param aClearanceValue = the clearance around the pad
 * @param aCircleToSegmentsCount = the number of segments to approximate a circle
 * @param aCorrectionFactor = the correction to apply to circles radius to keep
 * clearance when the circle is approxiamted by segment bigger or equal
 * to the real clearance value (usually near from 1.0)
 */
void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( std::vector <CPolyPt>& aCornerBuffer,
                                                        int                    aClearanceValue,
                                                        int                    aCircleToSegmentsCount,
                                                        double                 aCorrectionFactor )
{
    switch( m_Shape )
    {
    case S_CIRCLE:
        TransformArcToPolygon( aCornerBuffer, m_Start,         // Circle centre
                               m_End, 3600,
                               aCircleToSegmentsCount,
                               m_Width + (2 * aClearanceValue) );
        break;

    case S_ARC:
        TransformArcToPolygon( aCornerBuffer, m_Start,
                               m_End, m_Angle,
                               aCircleToSegmentsCount,
                               m_Width + (2 * aClearanceValue) );
        break;

    default:
        TransformRoundedEndsSegmentToPolygon( aCornerBuffer, m_Start, m_End,
                                              aCircleToSegmentsCount,
                                              m_Width + (2 * aClearanceValue) );
        break;
    }
}


/**
 * Function TransformShapeWithClearanceToPolygon
 * Convert the track shape to a closed polygon
 * Used in filling zones calculations
 * Circles (vias) and arcs (ends of tracks) are approximated by segments
 * @param aCornerBuffer = a buffer to store the polygon
 * @param aClearanceValue = the clearance around the pad
 * @param aCircleToSegmentsCount = the number of segments to approximate a circle
 * @param aCorrectionFactor = the correction to apply to circles radius to keep
 * clearance when the circle is approxiamted by segment bigger or equal
 * to the real clearance value (usually near from 1.0)
 */
void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCornerBuffer,
                                                   int                      aClearanceValue,
                                                   int                      aCircleToSegmentsCount,
                                                   double                   aCorrectionFactor )
{
    switch( Type() )
    {
    case PCB_VIA_T:
    {
        int radius = (m_Width / 2) + aClearanceValue;
        radius = KiROUND( radius * aCorrectionFactor );
        TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aCircleToSegmentsCount );
    }
        break;

    default:
        TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
                                              m_Start, m_End,
                                              aCircleToSegmentsCount,
                                              m_Width + ( 2 * aClearanceValue) );
        break;
    }
}


/**
 * Function TransformShapeWithClearanceToPolygon
 * Convert the pad shape to a closed polygon
 * Used in filling zones calculations
 * Circles and arcs are approximated by segments
 * @param aCornerBuffer = a buffer to store the polygon
 * @param aClearanceValue = the clearance around the pad
 * @param aCircleToSegmentsCount = the number of segments to approximate a circle
 * @param aCorrectionFactor = the correction to apply to circles radius to keep
 * clearance when the circle is approxiamted by segment bigger or equal
 * to the real clearance value (usually near from 1.0)
 */
void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCornerBuffer,
                                                   int                      aClearanceValue,
                                                   int                      aCircleToSegmentsCount,
                                                   double                   aCorrectionFactor )
{
    wxPoint corner_position;
    int     angle;
    int     dx = (m_Size.x / 2) + aClearanceValue;
    int     dy = (m_Size.y / 2) + aClearanceValue;

    int     delta = 3600 / aCircleToSegmentsCount;  // rot angle in 0.1 degree
    wxPoint PadShapePos = ReturnShapePos();         /* Note: for pad having a shape offset,
                                                     * the pad position is NOT the shape position */
    wxSize  psize = m_Size;                         /* pad size unsed in RECT and TRAPEZOIDAL pads
                                                     * trapezoidal pads are considered as rect
                                                     * pad shape having they boudary box size */

    switch( m_PadShape )
    {
    case PAD_CIRCLE:
        dx = (int) ( dx * aCorrectionFactor );
        TransformCircleToPolygon( aCornerBuffer, PadShapePos, dx,
                                  aCircleToSegmentsCount );
        break;

    case PAD_OVAL:
        // An oval pad has the same shape as a segment with rounded ends
        angle = m_Orient;
        {
        int width;
        wxPoint shape_offset;
        if( dy > dx )   // Oval pad X/Y ratio for choosing translation axis
        {
            dy = (int) ( dy * aCorrectionFactor );
            shape_offset.y = dy - dx;
            width = dx * 2;
        }
        else    //if( dy <= dx )
        {
            dx = (int) ( dx * aCorrectionFactor );
            shape_offset.x = dy - dx;
            width = dy * 2;
        }

        RotatePoint( &shape_offset, angle );
        wxPoint start = PadShapePos - shape_offset;
        wxPoint end = PadShapePos + shape_offset;
        TransformRoundedEndsSegmentToPolygon( aCornerBuffer, start, end,
                                              aCircleToSegmentsCount, width );
        }
        break;

    default:
    case PAD_TRAPEZOID:
        psize.x += std::abs( m_DeltaSize.y );
        psize.y += std::abs( m_DeltaSize.x );

    // fall through
    case PAD_RECT:
        // Easy implementation for rectangular cutouts with rounded corners
        angle = m_Orient;

        // Corner rounding radius
        int rounding_radius = (int) ( aClearanceValue * aCorrectionFactor );
        int angle_pg;  // Polygon increment angle

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        {
            corner_position = wxPoint( 0, -rounding_radius );
            RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );

            // Start at half increment offset
            angle_pg = i * delta;
            RotatePoint( &corner_position, angle_pg );

            // Rounding vector rotation
            corner_position -= psize / 2;            // Rounding vector + Pad corner offset
            RotatePoint( &corner_position, angle );

            // Rotate according to module orientation
            corner_position += PadShapePos;          // Shift origin to position
            CPolyPt polypoint( corner_position.x, corner_position.y );
            aCornerBuffer.push_back( polypoint );
        }

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        {
            corner_position = wxPoint( -rounding_radius, 0 );
            RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
            angle_pg = i * delta;
            RotatePoint( &corner_position, angle_pg );
            corner_position -= wxPoint( psize.x / 2, -psize.y / 2 );
            RotatePoint( &corner_position, angle );
            corner_position += PadShapePos;
            CPolyPt polypoint( corner_position.x, corner_position.y );
            aCornerBuffer.push_back( polypoint );
        }

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        {
            corner_position = wxPoint( 0, rounding_radius );
            RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
            angle_pg = i * delta;
            RotatePoint( &corner_position, angle_pg );
            corner_position += psize / 2;
            RotatePoint( &corner_position, angle );
            corner_position += PadShapePos;
            CPolyPt polypoint( corner_position.x, corner_position.y );
            aCornerBuffer.push_back( polypoint );
        }

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        {
            corner_position = wxPoint( rounding_radius, 0 );
            RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
            angle_pg = i * delta;
            RotatePoint( &corner_position, angle_pg );
            corner_position -= wxPoint( -psize.x / 2, psize.y / 2 );
            RotatePoint( &corner_position, angle );
            corner_position += PadShapePos;
            CPolyPt polypoint( corner_position.x, corner_position.y );
            aCornerBuffer.push_back( polypoint );
        }

        aCornerBuffer.back().end_contour = true;
        break;
    }
}


/**
 * Function CreateThermalReliefPadPolygon
 * Add holes around a pad to create a thermal relief
 * copper thickness is min (dx/2, aCopperWitdh) or min (dy/2, aCopperWitdh)
 * @param aCornerBuffer = a buffer to store the polygon
 * @param aPad     = the current pad used to create the thermal shape
 * @param aThermalGap = gap in thermal shape
 * @param aCopperThickness = stubs thickness in thermal shape
 * @param aMinThicknessValue = min copper thickness allowed
 * @param aCircleToSegmentsCount = the number of segments to approximate a circle
 * @param aCorrectionFactor = the correction to apply to circles radius to keep
 * @param aThermalRot = for rond pads the rotation of thermal stubs (450 usually for 45 deg.)
 */

/* thermal reliefs are created as 4 polygons.
 * each corner of a polygon if calculated for a pad at position 0, 0, orient 0,
 * and then moved and rotated acroding to the pad position and orientation
 */

/*
 * Note 1: polygons are drawm using outlines witk a thickness = aMinThicknessValue
 * so shapes must take in account this outline thickness
 *
 * Note 2:
 *      Trapezoidal pads are not considered here because they are very special case
 *      and are used in microwave applications and they *DO NOT* have a thermal relief that
 *      change the shape by creating stubs and destroy their properties.
 */
void    CreateThermalReliefPadPolygon( std::vector<CPolyPt>& aCornerBuffer,
                                       D_PAD&                aPad,
                                       int                   aThermalGap,
                                       int                   aCopperThickness,
                                       int                   aMinThicknessValue,
                                       int                   aCircleToSegmentsCount,
                                       double                aCorrectionFactor,
                                       int                   aThermalRot )
{
    wxPoint corner, corner_end;
    wxPoint PadShapePos = aPad.ReturnShapePos();    /* Note: for pad having a shape offset,
                                                     * the pad position is NOT the shape position */
    wxSize  copper_thickness;

    int     dx = aPad.GetSize().x / 2;
    int     dy = aPad.GetSize().y / 2;

    int     delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree

    /* Keep in account the polygon outline thickness
     * aThermalGap must be increased by aMinThicknessValue/2 because drawing external outline
     * with a thickness of aMinThicknessValue will reduce gap by aMinThicknessValue/2
     */
    aThermalGap += aMinThicknessValue / 2;

    /* Keep in account the polygon outline thickness
     * copper_thickness must be decreased by aMinThicknessValue because drawing outlines
     * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue
     */
    aCopperThickness -= aMinThicknessValue;

    if( aCopperThickness < 0 )
        aCopperThickness = 0;

    copper_thickness.x = std::min( dx, aCopperThickness );
    copper_thickness.y = std::min( dy, aCopperThickness );

    switch( aPad.GetShape() )
    {
    case PAD_CIRCLE:    // Add 4 similar holes
        {
            /* we create 4 copper holes and put them in position 1, 2, 3 and 4
             * here is the area of the rectangular pad + its thermal gap
             * the 4 copper holes remove the copper in order to create the thermal gap
             * 4 ------ 1
             * |        |
             * |        |
             * |        |
             * |        |
             * 3 ------ 2
             * holes 2, 3, 4 are the same as hole 1, rotated 90, 180, 270 deg
             */

            // Build the hole pattern, for the hole in the X >0, Y > 0 plane:
            // The pattern roughtly is a 90 deg arc pie
            std::vector <wxPoint> corners_buffer;

            // Radius of outer arcs of the shape corrected for arc approximation by lines
            int outer_radius = (int) ( (dx + aThermalGap) * aCorrectionFactor );

            // Crosspoint of thermal spoke sides, the first point of polygon buffer
            corners_buffer.push_back( wxPoint( copper_thickness.x / 2, copper_thickness.y / 2 ) );

            // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
            // and first seg of arc approx
            corner.x = copper_thickness.x / 2;
            int y = outer_radius - (aThermalGap / 4);
            corner.y = (int) sqrt( ( ( (double) y * y ) - (double) corner.x * corner.x ) );

            if( aThermalRot != 0 )
                corners_buffer.push_back( corner );

            // calculate the starting point of the outter arc
            corner.x = copper_thickness.x / 2;

            double dtmp = sqrt( ( (double) outer_radius * outer_radius ) -
                                ( (double) corner.x * corner.x ) );
            corner.y = (int) dtmp;
            RotatePoint( &corner, 90 );

            // calculate the ending point of the outter arc
            corner_end.x = corner.y;
            corner_end.y = corner.x;

            // calculate intermediate points (y coordinate from corner.y to corner_end.y
            while( (corner.y > corner_end.y)  && (corner.x < corner_end.x) )
            {
                corners_buffer.push_back( corner );
                RotatePoint( &corner, delta );
            }

            corners_buffer.push_back( corner_end );

            /* add an intermediate point, to avoid angles < 90 deg between last arc approx line
             * and radius line
             */
            corner.x = corners_buffer[1].y;
            corner.y = corners_buffer[1].x;
            corners_buffer.push_back( corner );

            // Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270  deg
            // aThermalRot = 450 (45.0 degrees orientation) work fine.
            int angle_pad = aPad.GetOrientation();              // Pad orientation
            int th_angle  = aThermalRot;

            for( unsigned ihole = 0; ihole < 4; ihole++ )
            {
                for( unsigned ii = 0; ii < corners_buffer.size(); ii++ )
                {
                    corner = corners_buffer[ii];
                    RotatePoint( &corner, th_angle + angle_pad );          // Rotate by segment angle and pad orientation
                    corner += PadShapePos;
                    aCornerBuffer.push_back( CPolyPt( corner.x, corner.y ) );
                }

                aCornerBuffer.back().end_contour = true;
                th_angle += 900;       // Note: th_angle in in 0.1 deg.
            }
        }
        break;

    case PAD_OVAL:
        {
            // Oval pad support along the lines of round and rectangular pads
            std::vector <wxPoint> corners_buffer;               // Polygon buffer as vector

            int     dx = (aPad.GetSize().x / 2) + aThermalGap;     // Cutout radius x
            int     dy = (aPad.GetSize().y / 2) + aThermalGap;     // Cutout radius y

            wxPoint shape_offset;

            // We want to calculate an oval shape with dx > dy.
            // if this is not the case, exchange dx and dy, and rotate the shape 90 deg.
            int supp_angle = 0;

            if( dx < dy )
            {
                EXCHG( dx, dy );
                supp_angle = 900;
                EXCHG( copper_thickness.x, copper_thickness.y );
            }

            int deltasize = dx - dy;        // = distance between shape position and the 2 demi-circle ends centre
            // here we have dx > dy
            // Radius of outer arcs of the shape:
            int outer_radius = dy;     // The radius of the outer arc is radius end + aThermalGap

            // Some coordinate fiddling, depending on the shape offset direction
            shape_offset = wxPoint( deltasize, 0 );

            // Crosspoint of thermal spoke sides, the first point of polygon buffer
            corner.x = copper_thickness.x / 2;
            corner.y = copper_thickness.y / 2;
            corners_buffer.push_back( corner );

            // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge
            // If copper thickness is more than shape offset, we need to calculate arc intercept point.
            if( copper_thickness.x > deltasize )
            {
                corner.x = copper_thickness.x / 2;
                corner.y =  (int) sqrt( ( (double) outer_radius * outer_radius ) -
                                        ( (double) ( corner.x - delta ) * ( corner.x - deltasize ) ) );
                corner.x -= deltasize;

                /* creates an intermediate point, to have a > 90 deg angle
                 * between the side and the first segment of arc approximation
                 */
                wxPoint intpoint = corner;
                intpoint.y -= aThermalGap / 4;
                corners_buffer.push_back( intpoint + shape_offset );
                RotatePoint( &corner, 90 );
            }
            else
            {
                corner.x = copper_thickness.x / 2;
                corner.y = outer_radius;
                corners_buffer.push_back( corner );
            }

            // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
            // and first seg of arc approx
            wxPoint last_corner;
            last_corner.y = copper_thickness.y / 2;
            int     px = outer_radius - (aThermalGap / 4);
            last_corner.x =
                (int) sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) );

            // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge
            corner_end.y = copper_thickness.y / 2;
            corner_end.x =
                (int) sqrt( ( (double) outer_radius *
                             outer_radius ) - ( (double) corner_end.y * corner_end.y ) );
            RotatePoint( &corner_end, -90 );

            // calculate intermediate arc points till limit is reached
            while( (corner.y > corner_end.y)  && (corner.x < corner_end.x) )
            {
                corners_buffer.push_back( corner + shape_offset );
                RotatePoint( &corner, delta );
            }

            //corners_buffer.push_back(corner + shape_offset);      // TODO: about one mil geometry error forms somewhere.
            corners_buffer.push_back( corner_end + shape_offset );
            corners_buffer.push_back( last_corner + shape_offset );         // Enabling the line above shows intersection point.

            /* Create 2 holes, rotated by pad rotation.
             */
            int angle = aPad.GetOrientation() + supp_angle;

            for( int irect = 0; irect < 2; irect++ )
            {
                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );
                    cpos += PadShapePos;
                    aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
                }

                aCornerBuffer.back().end_contour = true;
                angle += 1800;       // this is calculate hole 3

                if( angle >= 3600 )
                    angle -= 3600;
            }

            // Create holes, that are the mirrored from the previous holes
            for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
            {
                wxPoint swap = corners_buffer[ic];
                swap.x = -swap.x;
                corners_buffer[ic] = swap;
            }

            // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
            angle = aPad.GetOrientation() + supp_angle;

            for( int irect = 0; irect < 2; irect++ )
            {
                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );
                    cpos += PadShapePos;
                    aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
                }

                aCornerBuffer.back().end_contour = true;
                angle += 1800;

                if( angle >= 3600 )
                    angle -= 3600;
            }
        }
        break;

    case PAD_RECT:       // draw 4 Holes
        {
            /* we create 4 copper holes and put them in position 1, 2, 3 and 4
             * here is the area of the rectangular pad + its thermal gap
             * the 4 copper holes remove the copper in order to create the thermal gap
             * 4 ------ 1
             * |        |
             * |        |
             * |        |
             * |        |
             * 3 ------ 2
             * hole 3 is the same as hole 1, rotated 180 deg
             * hole 4 is the same as hole 2, rotated 180 deg and is the same as hole 1, mirrored
             */

            // First, create a rectangular hole for position 1 :
            // 2 ------- 3
            //  |        |
            //  |        |
            //  |        |
            // 1  -------4

            // Modified rectangles with one corner rounded. TODO: merging with oval thermals
            // and possibly round too.

            std::vector <wxPoint> corners_buffer;               // Polygon buffer as vector

            int dx = (aPad.GetSize().x / 2) + aThermalGap;         // Cutout radius x
            int dy = (aPad.GetSize().y / 2) + aThermalGap;         // Cutout radius y

            // The first point of polygon buffer is left lower corner, second the crosspoint of
            // thermal spoke sides, the third is upper right corner and the rest are rounding
            // vertices going anticlockwise. Note the inveted Y-axis in CG.
            corners_buffer.push_back( wxPoint( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ) );    // Adds small miters to zone
            corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) );    // fill and spoke corner
            corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) );
            corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) );
            corners_buffer.push_back( wxPoint( -(aThermalGap / 4 + copper_thickness.x / 2), -dy ) );

            int angle = aPad.GetOrientation();
            int rounding_radius = (int) ( aThermalGap * aCorrectionFactor );    // Corner rounding radius
            int angle_pg;                                                       // Polygon increment angle

            for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
            {
                wxPoint corner_position = wxPoint( 0, -rounding_radius );

                // Start at half increment offset
                RotatePoint( &corner_position, 1800 / aCircleToSegmentsCount );
                angle_pg = i * delta;

                RotatePoint( &corner_position, angle_pg );          // Rounding vector rotation
                corner_position -= aPad.GetSize() / 2;              // Rounding vector + Pad corner offset

                corners_buffer.push_back( wxPoint( corner_position.x, corner_position.y ) );
            }

            for( int irect = 0; irect < 2; irect++ )
            {
                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );            // Rotate according to module orientation
                    cpos += PadShapePos;                    // Shift origin to position
                    aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
                }

                aCornerBuffer.back().end_contour = true;
                angle += 1800;       // this is calculate hole 3

                if( angle >= 3600 )
                    angle -= 3600;
            }

            // Create holes, that are the mirrored from the previous holes
            for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
            {
                wxPoint swap = corners_buffer[ic];
                swap.x = -swap.x;
                corners_buffer[ic] = swap;
            }

            // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
            for( int irect = 0; irect < 2; irect++ )
            {
                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );
                    cpos += PadShapePos;
                    aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
                }

                aCornerBuffer.back().end_contour = true;
                angle += 1800;

                if( angle >= 3600 )
                    angle -= 3600;
            }

        }
        break;

    default:
        ;
    }
}
