Commit 4bba4b87 authored by jean-pierre charras's avatar jean-pierre charras
Browse files

Pcbnew: Allows trapezoidal pads in zones.

parent f4ca3d55
Loading
Loading
Loading
Loading
+136 −70
Original line number Diff line number Diff line
@@ -27,8 +27,8 @@
 * @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
/* Function to convert pad and track shapes to polygons
 * Used to fill zones areas and in 3D viewer
 */
#include <vector>

@@ -520,22 +520,19 @@ void TRACK:: TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer
 * clearance when the circle is approximated by segment bigger or equal
 * to the real clearance value (usually near from 1.0)
 */
#include <clipper.hpp>
void D_PAD:: TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
                                                   int             aClearanceValue,
                                                   int             aCircleToSegmentsCount,
                                                   double          aCorrectionFactor ) const
{
    wxPoint corner_position;
    double  angle;
    double  angle = m_Orient;
    int     dx = (m_Size.x / 2) + aClearanceValue;
    int     dy = (m_Size.y / 2) + aClearanceValue;

    double  delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
    wxPoint PadShapePos = ShapePos();               /* 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( GetShape() )
    {
@@ -547,7 +544,6 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer

    case PAD_OVAL:
        // An oval pad has the same shape as a segment with rounded ends
        angle = m_Orient;
        {
        int width;
        wxPoint shape_offset;
@@ -573,77 +569,51 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer
        break;

    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 = KiROUND( aClearanceValue * aCorrectionFactor );
        double angle_pg;  // Polygon increment angle

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
    {
            corner_position = wxPoint( 0, -rounding_radius );
            RotatePoint( &corner_position, (1800.0 / aCircleToSegmentsCount) );
        wxPoint corners[4];
        BuildPadPolygon( corners, wxSize( 0, 0 ), angle );

            // Start at half increment offset
            angle_pg = i * delta;
            RotatePoint( &corner_position, angle_pg );
        // We are using ClipperLib to inflate the polygon shape, using
        // arcs to connect moved segments.
        ClipperLib::Path outline;
        ClipperLib::Paths shapeWithClearance;

            // Rounding vector rotation
            corner_position -= psize / 2;            // Rounding vector + Pad corner offset
            RotatePoint( &corner_position, angle );
        for( int ii = 0; ii < 4; ii++ )
            outline << ClipperLib::IntPoint( corners[ii].x, corners[ii].y );

            // Rotate according to module orientation
            corner_position += PadShapePos;          // Shift origin to position
            CPolyPt polypoint( corner_position.x, corner_position.y );
            aCornerBuffer.Append( polypoint );
        }
        ClipperLib::ClipperOffset offset_engine;
        // Prepare an offset (inflate) transform, with edges connected by arcs
        offset_engine.AddPath( outline, ClipperLib::jtRound, ClipperLib::etClosedPolygon );

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        {
            corner_position = wxPoint( -rounding_radius, 0 );
            RotatePoint( &corner_position, (1800.0 / 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.Append( polypoint );
        }
        // Clipper approximates arcs by segments
        // It uses a value called ArcTolerance which is the max error between the arc
        // and segments created to approximate this arc
        // the number of segm per circle is:
        // n = PI / acos(1 - arc_tolerance / (arc radius))
        // the arc radius is aClearanceValue
        // because arc_tolerance is << aClearanceValue and aClearanceValue >= 0
        // n = PI / (arc_tolerance / aClearanceValue )
        offset_engine.ArcTolerance = (double)aClearanceValue / 3.14 / aCircleToSegmentsCount;

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        {
            corner_position = wxPoint( 0, rounding_radius );
            RotatePoint( &corner_position, (1800.0 / 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.Append( polypoint );
        }
        double rounding_radius = aClearanceValue * aCorrectionFactor;
        offset_engine.Execute( shapeWithClearance, rounding_radius );

        for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
        // get new outline (only one polygon is expected)
        // For info, ClipperLib uses long long to handle integer coordinates
        ClipperLib::Path& polygon = shapeWithClearance[0];

        for( unsigned jj = 0; jj < polygon.size(); jj++ )
        {
            corner_position = wxPoint( rounding_radius, 0 );
            RotatePoint( &corner_position, (1800.0 / 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.x = int( polygon[jj].X );
            corner_position.y = int( polygon[jj].Y );
            corner_position += PadShapePos;
            CPolyPt polypoint( corner_position.x, corner_position.y );
            aCornerBuffer.Append( polypoint );
        }

        aCornerBuffer.CloseLastContour();
    }
        break;
    }
}
@@ -1090,9 +1060,105 @@ void CreateThermalReliefPadPolygon( CPOLYGONS_LIST& aCornerBuffer,
                aCornerBuffer.CloseLastContour();
                angle = AddAngles( angle, 1800 );
            }
        }
        break;

    case PAD_TRAPEZOID:
        {
        CPOLYGONS_LIST cbuffer;
        // We need a length to build the stubs of the thermal reliefs
        // the value is not very important. The pad bounding box gives a reasonable value
        EDA_RECT bbox = aPad.GetBoundingBox();
        int stub_len = std::max( bbox.GetWidth(), bbox.GetHeight() );

        aPad.TransformShapeWithClearanceToPolygon( cbuffer, aThermalGap,
                    aCircleToSegmentsCount, aCorrectionFactor );

        // We are using ClipperLib to substract stubs to clearance area (antipad area).
        ClipperLib::Path antipad;       // The full antipad area
        ClipperLib::Path stub;          // A basic stub ( a rectangle)
        ClipperLib::Paths stubs;        // the full stubs shape
        ClipperLib::Paths thermalShape; // the holes in copper zone

        // cbuffer is expected to contain only one polygon, which is
        // area of the pad + the thermal gap (the antipad)
        for( unsigned ii = 0; ii < cbuffer.GetCornersCount(); ii++ )
            antipad << ClipperLib::IntPoint( cbuffer.GetPos(ii).x, cbuffer.GetPos(ii).y );

        // We now substract the stubs (connections to the copper zone)
        ClipperLib::Clipper clip_engine;
        // Prepare a clipping transform
        clip_engine.AddPath( antipad, ClipperLib::ptSubject, true );

        // Create stubs and add them to clipper engine
        wxPoint stubBuffer[4];
        stubBuffer[0].x = stub_len;
        stubBuffer[0].y = copper_thickness.y/2;
        stubBuffer[1] = stubBuffer[0];
        stubBuffer[1].y = -copper_thickness.y/2;
        stubBuffer[2] = stubBuffer[1];
        stubBuffer[2].x = -stub_len;
        stubBuffer[3] = stubBuffer[2];
        stubBuffer[3].y = copper_thickness.y/2;

        for( unsigned ii = 0; ii < DIM( stubBuffer ); ii++ )
        {
            wxPoint cpos = stubBuffer[ii];
            RotatePoint( &cpos, aPad.GetOrientation() );
            cpos += PadShapePos;
            stub << ClipperLib::IntPoint( cpos.x, cpos.y );
        }

        ClipperLib::Clipper stubs_engine;
        stubs_engine.AddPath( stub, ClipperLib::ptSubject, true );

        stubBuffer[0].y = stub_len;
        stubBuffer[0].x = copper_thickness.x/2;
        stubBuffer[1] = stubBuffer[0];
        stubBuffer[1].x = -copper_thickness.x/2;
        stubBuffer[2] = stubBuffer[1];
        stubBuffer[2].y = -stub_len;
        stubBuffer[3] = stubBuffer[2];
        stubBuffer[3].x = copper_thickness.x/2;
        stub.clear();

        for( unsigned ii = 0; ii < DIM( stubBuffer ); ii++ )
        {
            wxPoint cpos = stubBuffer[ii];
            RotatePoint( &cpos, aPad.GetOrientation() );
            cpos += PadShapePos;
            stub << ClipperLib::IntPoint( cpos.x, cpos.y );
        }

        stubs_engine.AddPath( stub, ClipperLib::ptClip, true );

        // Build the full stubs shape:
        stubs_engine.Execute( ClipperLib::ctUnion, stubs );

        // remove stubs to antipad area (i.e. add copper stubs)
        clip_engine.AddPath( stubs[0], ClipperLib::ptClip, true );
        clip_engine.Execute( ClipperLib::ctDifference, thermalShape );

        // put thermal shapes (holes) to list:
        wxPoint corner_position;

        for( unsigned ii = 0; ii < thermalShape.size(); ii++ )
        {
            ClipperLib::Path& polygon = thermalShape[ii];

            for( unsigned jj = 0; jj < polygon.size(); jj++ )
            {
                corner_position.x = int( polygon[jj].X );
                corner_position.y = int( polygon[jj].Y );
                CPolyPt polypoint( corner_position.x, corner_position.y );
                aCornerBuffer.Append( polypoint );
            }

            aCornerBuffer.CloseLastContour();
        }

        break;
    }

    default:
        ;
+1 −5
Original line number Diff line number Diff line
@@ -249,11 +249,7 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb )
                continue;
            }

            if( ( GetPadConnection( pad ) == PAD_NOT_IN_ZONE )
                || ( pad->GetShape() == PAD_TRAPEZOID ) )

            // PAD_TRAPEZOID shapes are not in zones because they are used in microwave apps
            // and i think it is good that shapes are not changed by thermal pads or others
            if( GetPadConnection( pad ) == PAD_NOT_IN_ZONE )
            {
                int gap = zone_clearance;
                int thermalGap = GetThermalReliefGap( pad );