diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 84bb09f3e5..29268ba495 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -128,8 +128,6 @@ void MODULE::TransformPadsShapesWithClearanceToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, int aMaxError, bool aSkipNPTHPadsWihNoCopper ) const { - wxSize margin; - for( auto pad : m_pads ) { if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) ) @@ -159,26 +157,27 @@ void MODULE::TransformPadsShapesWithClearanceToPolygon( PCB_LAYER_ID aLayer, } } + wxSize margin; + int clearance = aInflateValue; + switch( aLayer ) { case F_Mask: case B_Mask: - margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; + clearance += pad->GetSolderMaskMargin(); break; case F_Paste: case B_Paste: margin = pad->GetSolderPasteMargin(); - margin.x += aInflateValue; - margin.y += aInflateValue; + clearance += ( margin.x + margin.y ) / 2; break; default: - margin.x = margin.y = aInflateValue; break; } - pad->BuildPadShapePolygon( aCornerBuffer, margin ); + pad->TransformShapeWithClearanceToPolygon( aCornerBuffer, clearance ); } } @@ -769,501 +768,6 @@ bool D_PAD::BuildPadDrillShapePolygon( return true; } -/** - * 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 aError = maximum error allowed when approximating arcs - * @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( SHAPE_POLY_SET& aCornerBuffer, - const D_PAD& aPad, - int aThermalGap, - int aCopperThickness, - int aMinThicknessValue, - int aError, - double aThermalRot ) -{ - wxPoint corner, corner_end; - wxSize copper_thickness; - wxPoint padShapePos = aPad.ShapePos(); // Note: for pad having a shape offset, - // the pad position is NOT the shape position - - /* 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 - */ - int dx = aPad.GetSize().x / 2; - int dy = aPad.GetSize().y / 2; - - copper_thickness.x = std::min( aPad.GetSize().x, aCopperThickness ) - aMinThicknessValue; - copper_thickness.y = std::min( aPad.GetSize().y, aCopperThickness ) - aMinThicknessValue; - - if( copper_thickness.x < 0 ) - copper_thickness.x = 0; - - if( copper_thickness.y < 0 ) - copper_thickness.y = 0; - - switch( aPad.GetShape() ) - { - case PAD_SHAPE_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 corners_buffer; - - int numSegs = std::max( GetArcToSegmentCount( dx + aThermalGap, aError, 360.0 ), - 8 ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - double delta = 3600.0 / numSegs; - - // Radius of outer arcs of the shape corrected for arc approximation by lines - int outer_radius = KiROUND( ( dx + aThermalGap ) * correction ); - - // 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 = KiROUND( 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; - - corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) - - ( (double) corner.x * corner.x ) ) ); - RotatePoint( &corner, 90 ); // 9 degrees is the spoke fillet size - - // calculate the ending point of the outer 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. - double angle_pad = aPad.GetOrientation(); // Pad orientation - double th_angle = aThermalRot; - - for( unsigned ihole = 0; ihole < 4; ihole++ ) - { - aCornerBuffer.NewOutline(); - - 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.Append( corner.x, corner.y ); - } - - th_angle += 900; // Note: th_angle in in 0.1 deg. - } - } - break; - - case PAD_SHAPE_OVAL: - { - // Oval pad support along the lines of round and rectangular pads - std::vector corners_buffer; // Polygon buffer as vector - - dx = (aPad.GetSize().x / 2) + aThermalGap; // Cutout radius x - 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 ) - { - std::swap( dx, dy ); - supp_angle = 900; - std::swap( 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 - - - int numSegs = std::max( GetArcToSegmentCount( outer_radius, aError, 360.0 ), 6 ); - double delta = 3600.0 / numSegs; - - // 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 = KiROUND( 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 ); // 9 degrees of thermal fillet - } - 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 = - KiROUND( 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 = - KiROUND( sqrt( ( (double) outer_radius * - outer_radius ) - ( (double) corner_end.y * corner_end.y ) ) ); - RotatePoint( &corner_end, -90 ); // 9 degrees of thermal fillet - - // 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. - */ - double angle = aPad.GetOrientation() + supp_angle; - - for( int irect = 0; irect < 2; irect++ ) - { - aCornerBuffer.NewOutline(); - for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) - { - wxPoint cpos = corners_buffer[ic]; - RotatePoint( &cpos, angle ); - cpos += padShapePos; - aCornerBuffer.Append( cpos.x, cpos.y ); - } - - angle = AddAngles( angle, 1800 ); // this is calculate hole 3 - } - - // 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++ ) - { - aCornerBuffer.NewOutline(); - - for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) - { - wxPoint cpos = corners_buffer[ic]; - RotatePoint( &cpos, angle ); - cpos += padShapePos; - aCornerBuffer.Append( cpos.x, cpos.y ); - } - - angle = AddAngles( angle, 1800 ); - } - } - break; - - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_ROUNDRECT: // thermal shape is the same for rectangular shapes. - case PAD_SHAPE_RECT: - { - /* 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 - * 1 ------ 4 - * | | - * | | - * | | - * | | - * 2 ------ 3 - * 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 corners_buffer; // Polygon buffer as vector - - dx = (aPad.GetSize().x / 2) + aThermalGap; // Cutout radius x - dy = (aPad.GetSize().y / 2) + aThermalGap; // Cutout radius y - - // calculation is optimized for pad shape with dy >= dx (vertical rectangle). - // if it is not the case, just rotate this shape 90 degrees: - double angle = aPad.GetOrientation(); - wxPoint corner_origin_pos( -aPad.GetSize().x / 2, -aPad.GetSize().y / 2 ); - - if( dy < dx ) - { - std::swap( dx, dy ); - std::swap( copper_thickness.x, copper_thickness.y ); - std::swap( corner_origin_pos.x, corner_origin_pos.y ); - angle += 900.0; - } - // Now calculate the hole pattern in position 1 ( top left pad corner ) - - // 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 inverted Y-axis in corners_buffer y coordinates. - wxPoint arc_end_point( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ); - corners_buffer.push_back( arc_end_point ); // 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) ) ); - // The first point to build the rounded corner: - wxPoint arc_start_point( -(aThermalGap / 4 + copper_thickness.x / 2) , -dy ); - corners_buffer.push_back( arc_start_point ); - - int numSegs = std::max( GetArcToSegmentCount( aThermalGap, aError, 360.0 ), 6 ); - double correction = GetCircletoPolyCorrectionFactor( numSegs ); - int rounding_radius = KiROUND( aThermalGap * correction ); // Corner rounding radius - - // Calculate arc angle parameters. - // the start angle id near 900 decidegrees, the final angle is near 1800.0 decidegrees. - double arc_increment = 3600.0 / numSegs; - - // the arc_angle_start is 900.0 or slighly more, depending on the actual arc starting point - double arc_angle_start = atan2( -arc_start_point.y -corner_origin_pos.y, arc_start_point.x - corner_origin_pos.x ) * 1800/M_PI; - if( arc_angle_start < 900.0 ) - arc_angle_start = 900.0; - - bool first_point = true; - for( double curr_angle = arc_angle_start; ; curr_angle += arc_increment ) - { - wxPoint corner_position = wxPoint( rounding_radius, 0 ); - RotatePoint( &corner_position, curr_angle ); // Rounding vector rotation - corner_position += corner_origin_pos; // Rounding vector + Pad corner offset - - // The arc angle is <= 90 degrees, therefore the arc is finished if the x coordinate - // decrease or the y coordinate is smaller than the y end point - if( !first_point && - ( corner_position.x >= corners_buffer.back().x || corner_position.y > arc_end_point.y ) ) - break; - - first_point = false; - - // Note: for hole in position 1, arc x coordinate is always < x starting point - // and arc y coordinate is always <= y ending point - if( corner_position != corners_buffer.back() // avoid duplicate corners. - && corner_position.x <= arc_start_point.x ) // skip current point at the right of the starting point - corners_buffer.push_back( corner_position ); - } - - for( int irect = 0; irect < 2; irect++ ) - { - aCornerBuffer.NewOutline(); - - 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.Append( cpos.x, cpos.y ); - } - - angle = AddAngles( angle, 1800 ); // this is calculate hole 3 - } - - // 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++ ) - { - aCornerBuffer.NewOutline(); - - for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) - { - wxPoint cpos = corners_buffer[ic]; - RotatePoint( &cpos, angle ); - cpos += padShapePos; - aCornerBuffer.Append( cpos.x, cpos.y ); - } - - angle = AddAngles( angle, 1800 ); - } - } - break; - - case PAD_SHAPE_TRAPEZOID: - { - SHAPE_POLY_SET antipad; // The full antipad area - - // 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( antipad, aThermalGap ); - - SHAPE_POLY_SET stub; // A basic stub ( a rectangle) - SHAPE_POLY_SET stubs; // the full stubs shape - - - // 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; - - stub.NewOutline(); - - for( unsigned ii = 0; ii < arrayDim( stubBuffer ); ii++ ) - { - wxPoint cpos = stubBuffer[ii]; - RotatePoint( &cpos, aPad.GetOrientation() ); - cpos += padShapePos; - stub.Append( cpos.x, cpos.y ); - } - - stubs.Append( stub ); - - 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.RemoveAllContours(); - stub.NewOutline(); - - for( unsigned ii = 0; ii < arrayDim( stubBuffer ); ii++ ) - { - wxPoint cpos = stubBuffer[ii]; - RotatePoint( &cpos, aPad.GetOrientation() ); - cpos += padShapePos; - stub.Append( cpos.x, cpos.y ); - } - - stubs.Append( stub ); - stubs.Simplify( SHAPE_POLY_SET::PM_FAST ); - - antipad.BooleanSubtract( stubs, SHAPE_POLY_SET::PM_FAST ); - aCornerBuffer.Append( antipad ); - - break; - } - - default: - ; - } -} void ZONE_CONTAINER::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, int aError, bool ignoreLineWidth ) const diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 2d34f573b9..2019584f80 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -732,180 +732,90 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) m_gal->SetFillColor( color ); } - m_gal->Save(); - m_gal->Translate( VECTOR2D( aPad->GetPosition() ) ); - m_gal->Rotate( -aPad->GetOrientationRadians() ); - - int custom_margin = 0; // a clearance/margin for custom shape, for solder paste/mask - // Choose drawing settings depending on if we are drawing a pad itself or a hole if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES ) { + m_gal->Save(); + m_gal->Translate( VECTOR2D( aPad->GetPosition() ) ); + m_gal->Rotate( -aPad->GetOrientationRadians() ); + // Drawing hole: has same shape as PAD_CIRCLE or PAD_OVAL size = getDrillSize( aPad ) / 2.0; - shape = getDrillShape( aPad ) == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE; - } - else if( aLayer == F_Mask || aLayer == B_Mask ) - { - // Drawing soldermask - int soldermaskMargin = aPad->GetSolderMaskMargin(); - custom_margin = soldermaskMargin; - m_gal->Translate( VECTOR2D( aPad->GetOffset() ) ); - size = VECTOR2D( aPad->GetSize().x / 2.0 + soldermaskMargin, - aPad->GetSize().y / 2.0 + soldermaskMargin ); - shape = aPad->GetShape(); - } - else if( aLayer == F_Paste || aLayer == B_Paste ) - { - // Drawing solderpaste - wxSize solderpasteMargin = aPad->GetSolderPasteMargin(); - // try to find a clearance which can be used for custom shapes - custom_margin = solderpasteMargin.x; + if( getDrillShape( aPad ) == PAD_DRILL_SHAPE_OBLONG ) + { + if( size.y >= size.x ) + { + m = ( size.y - size.x ); + n = size.x; - m_gal->Translate( VECTOR2D( aPad->GetOffset() ) ); - size = VECTOR2D( aPad->GetSize().x / 2.0 + solderpasteMargin.x, - aPad->GetSize().y / 2.0 + solderpasteMargin.y ); - shape = aPad->GetShape(); + m_gal->DrawArc( VECTOR2D( 0, -m ), n, -M_PI, 0 ); + m_gal->DrawArc( VECTOR2D( 0, m ), n, M_PI, 0 ); + + if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] ) + { + m_gal->DrawLine( VECTOR2D( -n, -m ), VECTOR2D( -n, m ) ); + m_gal->DrawLine( VECTOR2D( n, -m ), VECTOR2D( n, m ) ); + } + else + { + m_gal->DrawRectangle( VECTOR2D( -n, -m ), VECTOR2D( n, m ) ); + } + } + else + { + m = ( size.x - size.y ); + n = size.y; + m_gal->DrawArc( VECTOR2D( -m, 0 ), n, M_PI / 2, 3 * M_PI / 2 ); + m_gal->DrawArc( VECTOR2D( m, 0 ), n, M_PI / 2, -M_PI / 2 ); + + if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] ) + { + m_gal->DrawLine( VECTOR2D( -m, -n ), VECTOR2D( m, -n ) ); + m_gal->DrawLine( VECTOR2D( -m, n ), VECTOR2D( m, n ) ); + } + else + { + m_gal->DrawRectangle( VECTOR2D( -m, -n ), VECTOR2D( m, n ) ); + } + } + } + else + { + m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size.x ); + } + + m_gal->Restore(); } else - { - // Drawing every kind of pad - m_gal->Translate( VECTOR2D( aPad->GetOffset() ) ); - size = VECTOR2D( aPad->GetSize() ) / 2.0; - shape = aPad->GetShape(); - } - - switch( shape ) - { - case PAD_SHAPE_OVAL: - if( size.y >= size.x ) - { - m = ( size.y - size.x ); - n = size.x; - - m_gal->DrawArc( VECTOR2D( 0, -m ), n, -M_PI, 0 ); - m_gal->DrawArc( VECTOR2D( 0, m ), n, M_PI, 0 ); - - if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] ) - { - m_gal->DrawLine( VECTOR2D( -n, -m ), VECTOR2D( -n, m ) ); - m_gal->DrawLine( VECTOR2D( n, -m ), VECTOR2D( n, m ) ); - } - else - { - m_gal->DrawRectangle( VECTOR2D( -n, -m ), VECTOR2D( n, m ) ); - } - } - else - { - m = ( size.x - size.y ); - n = size.y; - m_gal->DrawArc( VECTOR2D( -m, 0 ), n, M_PI / 2, 3 * M_PI / 2 ); - m_gal->DrawArc( VECTOR2D( m, 0 ), n, M_PI / 2, -M_PI / 2 ); - - if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] ) - { - m_gal->DrawLine( VECTOR2D( -m, -n ), VECTOR2D( m, -n ) ); - m_gal->DrawLine( VECTOR2D( -m, n ), VECTOR2D( m, n ) ); - } - else - { - m_gal->DrawRectangle( VECTOR2D( -m, -n ), VECTOR2D( m, n ) ); - } - } - break; - - case PAD_SHAPE_RECT: - m_gal->DrawRectangle( VECTOR2D( -size.x, -size.y ), VECTOR2D( size.x, size.y ) ); - break; - - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_CHAMFERED_RECT: { SHAPE_POLY_SET polySet; - wxSize prsize( size.x * 2, size.y * 2 ); // size is the half pad area size) - const int corner_radius = aPad->GetRoundRectCornerRadius( prsize ); - bool doChamfer = shape == PAD_SHAPE_CHAMFERED_RECT; - auto board = aPad->GetBoard(); - int maxError = ARC_HIGH_DEF; + wxSize margin; + int clearance = 0; - if( board ) - maxError = board->GetDesignSettings().m_MaxError; - - TransformRoundChamferedRectToPolygon( polySet, wxPoint( 0, 0 ), prsize, - 0.0, corner_radius, aPad->GetChamferRectRatio(), - doChamfer ? aPad->GetChamferPositions() : 0, maxError ); - m_gal->DrawPolygon( polySet ); - break; - } - - case PAD_SHAPE_CUSTOM: - { // Draw the complex custom shape - - // Use solder[Paste/Mask]size or pad size to build pad shape - // however, solder[Paste/Mask] size has no actual meaning for a - // custom shape, because it is a set of basic shapes - // We use the custom_margin (good for solder mask, but approximative - // for solder paste). - if( custom_margin ) + switch( aLayer ) { - auto board = aPad->GetBoard(); - int maxError = ARC_HIGH_DEF; + case F_Mask: + case B_Mask: + clearance += aPad->GetSolderMaskMargin(); + break; - if( board ) - maxError = board->GetDesignSettings().m_MaxError; + case F_Paste: + case B_Paste: + margin = aPad->GetSolderPasteMargin(); + clearance += ( margin.x + margin.y ) / 2; + break; - SHAPE_POLY_SET outline; - outline.Append( aPad->GetCustomShapeAsPolygon() ); - // outline polygon can have holes linked to the main outline. - // So use InflateWithLinkedHoles(), not Inflate() that can create - // bad shapes if custom_margin is < 0 - int numSegs = std::max( GetArcToSegmentCount( custom_margin, maxError, 360.0 ), 6 ); - outline.InflateWithLinkedHoles( custom_margin, numSegs, SHAPE_POLY_SET::PM_FAST ); - m_gal->DrawPolygon( outline ); + default: + break; } - else - { - // Draw the polygon: only one polygon is expected - // However we provide a multi polygon shape drawing - // ( for the future or to show even an incorrect shape - m_gal->DrawPolygon( aPad->GetCustomShapeAsPolygon() ); - } - } - break; - - case PAD_SHAPE_TRAPEZOID: - { - std::deque pointList; - wxPoint corners[4]; - - VECTOR2D padSize = VECTOR2D( aPad->GetSize().x, aPad->GetSize().y ) / 2; - VECTOR2D deltaPadSize = size - padSize; // = solder[Paste/Mask]Margin or 0 - - aPad->BuildPadPolygon( corners, wxSize( deltaPadSize.x, deltaPadSize.y ), 0.0 ); - SHAPE_POLY_SET polySet; - polySet.NewOutline(); - polySet.Append( VECTOR2I( corners[0] ) ); - polySet.Append( VECTOR2I( corners[1] ) ); - polySet.Append( VECTOR2I( corners[2] ) ); - polySet.Append( VECTOR2I( corners[3] ) ); + aPad->TransformShapeWithClearanceToPolygon( polySet, clearance ); m_gal->DrawPolygon( polySet ); } - break; - - case PAD_SHAPE_CIRCLE: - m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size.x ); - break; - } - - m_gal->Restore(); // Clearance lines - // It has to be called after GAL::Restore() as TransformShapeWithClearanceToPolygon() - // returns already transformed coordinates - constexpr int clearanceFlags = /*PCB_RENDER_SETTINGS::CL_EXISTING |*/ PCB_RENDER_SETTINGS::CL_PADS; + constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_PADS; if( ( m_pcbSettings.m_clearance & clearanceFlags ) == clearanceFlags && ( aLayer == LAYER_PAD_FR