void PSLIKE_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, EDA_DRAW_MODE_T modetrace ) { wxASSERT( outputFile ); int x0, y0, x1, y1, delta; wxSize size( aSize ); // The pad is reduced to an oval by dy > dx if( size.x > size.y ) { EXCHG( size.x, size.y ); orient = AddAngles( orient, 900 ); } delta = size.y - size.x; x0 = 0; y0 = -delta / 2; x1 = 0; y1 = delta / 2; RotatePoint( &x0, &y0, orient ); RotatePoint( &x1, &y1, orient ); if( modetrace == FILLED ) ThickSegment( wxPoint( pos.x + x0, pos.y + y0 ), wxPoint( pos.x + x1, pos.y + y1 ), size.x, modetrace ); else sketchOval( pos, size, orient, -1 ); }
/* Plot oval pad. */ void HPGL_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, EDA_DRAW_MODE_T trace_mode ) { wxASSERT( outputFile ); int deltaxy, cx, cy; wxSize size( aSize ); /* The pad will be drawn as an oblong shape with size.y > size.x * (Oval vertical orientation 0) */ if( size.x > size.y ) { std::swap( size.x, size.y ); orient = AddAngles( orient, 900 ); } deltaxy = size.y - size.x; // distance between centers of the oval if( trace_mode == FILLED ) { FlashPadRect( pos, wxSize( size.x, deltaxy + KiROUND( penDiameter ) ), orient, trace_mode ); cx = 0; cy = deltaxy / 2; RotatePoint( &cx, &cy, orient ); FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode ); cx = 0; cy = -deltaxy / 2; RotatePoint( &cx, &cy, orient ); FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode ); } else // Plot in outline mode. { sketchOval( pos, size, orient, KiROUND( penDiameter ) ); } }
void PSLIKE_PLOTTER::FlashPadOval( const wxPoint& aPadPos, const wxSize& aSize, double aPadOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) { wxASSERT( outputFile ); int x0, y0, x1, y1, delta; wxSize size( aSize ); // The pad is reduced to an oval by dy > dx if( size.x > size.y ) { std::swap( size.x, size.y ); aPadOrient = AddAngles( aPadOrient, 900 ); } delta = size.y - size.x; x0 = 0; y0 = -delta / 2; x1 = 0; y1 = delta / 2; RotatePoint( &x0, &y0, aPadOrient ); RotatePoint( &x1, &y1, aPadOrient ); if( aTraceMode == FILLED ) ThickSegment( wxPoint( aPadPos.x + x0, aPadPos.y + y0 ), wxPoint( aPadPos.x + x1, aPadPos.y + y1 ), size.x, aTraceMode, NULL ); else sketchOval( aPadPos, size, aPadOrient, -1 ); }
/** * DXF oval pad: always done in sketch mode */ void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, EDA_DRAW_MODE_T trace_mode ) { wxASSERT( outputFile ); wxSize size( aSize ); /* The chip is reduced to an oval tablet with size.y > size.x * (Oval vertical orientation 0) */ if( size.x > size.y ) { std::swap( size.x, size.y ); orient = AddAngles( orient, 900 ); } sketchOval( pos, size, orient, -1 ); }
void PLOTTER::sketchOval( const wxPoint& pos, const wxSize& aSize, double orient, int width ) { SetCurrentLineWidth( width ); width = currentPenWidth; int radius, deltaxy, cx, cy; wxSize size( aSize ); if( size.x > size.y ) { std::swap( size.x, size.y ); orient = AddAngles( orient, 900 ); } deltaxy = size.y - size.x; /* distance between centers of the oval */ radius = ( size.x - width ) / 2; cx = -radius; cy = -deltaxy / 2; RotatePoint( &cx, &cy, orient ); MoveTo( wxPoint( cx + pos.x, cy + pos.y ) ); cx = -radius; cy = deltaxy / 2; RotatePoint( &cx, &cy, orient ); FinishTo( wxPoint( cx + pos.x, cy + pos.y ) ); cx = radius; cy = -deltaxy / 2; RotatePoint( &cx, &cy, orient ); MoveTo( wxPoint( cx + pos.x, cy + pos.y ) ); cx = radius; cy = deltaxy / 2; RotatePoint( &cx, &cy, orient ); FinishTo( wxPoint( cx + pos.x, cy + pos.y ) ); cx = 0; cy = deltaxy / 2; RotatePoint( &cx, &cy, orient ); Arc( wxPoint( cx + pos.x, cy + pos.y ), orient + 1800, orient + 3600, radius, NO_FILL ); cx = 0; cy = -deltaxy / 2; RotatePoint( &cx, &cy, orient ); Arc( wxPoint( cx + pos.x, cy + pos.y ), orient, orient + 1800, radius, NO_FILL ); }
void convertOblong2Segment( wxSize aSize, double aOrient, wxPoint& aStart, wxPoint& aEnd ) { wxSize size( aSize ); double orient = aOrient; /* The pad will be drawn as an oblong shape with size.y > size.x * (Oval vertical orientation 0) */ if( size.x > size.y ) { std::swap( size.x, size.y ); orient = AddAngles( orient, 900 ); } int deltaxy = size.y - size.x; // distance between centers of the oval int cx = 0; int cy = deltaxy / 2; RotatePoint( &cx, &cy, orient ); aStart = wxPoint( cx, cy ); cx = 0; cy = -deltaxy / 2; RotatePoint( &cx, &cy, orient ); aEnd = wxPoint( cx, cy ); }
/* 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 int r; 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; wxPoint startPoint; wxPoint 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_ROUNDRECT: // a round rect is a smaller rect, with a clearance augmented by the corners radius r = aPad->GetRoundRectCornerRadius(); padHalfsize.x -= r; padHalfsize.y -= r; distToLine += r; // Fall through 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( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ), wxPoint(m_segmLength,0), distToLine ) ) return false; break; } } return true; }
/* * 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 <wxPoint> corners_buffer; int numSegs = std::max( GetArcToSegmentCount( dx + aThermalGap, aError, 360.0 ), 6 ); 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 <wxPoint> 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 <wxPoint> 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: ; } }
/* 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; }