Hi everybody, below is the fourth version of a patch to add support for rectangular pads with rounded corners to KiCad. The patch was generated against revision 5464.
What is new: - export to VRML works, - export to Specctra DSN works, at least Freeroute reads and displays it, - pad to pad DRC is implemented with proper PAD_ROUNDRECT support, at least I think I got it right, - in the process I fixed what I think was a bug in trapezoid2pointDRC(), - I changed the way generating the module report works to how I believe it is supposed to work. What is still to do: - GenCAD export, I do not understand what this is used for and couldn't find a viewer that would show something I could recognize, so PAD_ROUNDRECT is still handled the same way as PAD_TRAPEZOID, - proper support in the PNS router, without a SHAPE_CONVEX nothing can be done about that (I might look into that). This will be the last version of this patch for now, unless somebody points out a bug or some missing piece. IMHO this is now usable, so if you want it in KiCad... ;-) Enjoy, MGri === modified file 'common/common_plotDXF_functions.cpp' --- common/common_plotDXF_functions.cpp 2015-02-26 10:33:15 +0000 +++ common/common_plotDXF_functions.cpp 2015-03-01 22:25:20 +0000 @@ -623,6 +623,12 @@ FinishTo( wxPoint( ox, oy ) ); } +void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ) +{ + // Not actually needed, because for DXF copper is plotted entirely as polygons +} /** * DXF trapezoidal pad: only sketch mode is supported === modified file 'common/common_plotGERBER_functions.cpp' --- common/common_plotGERBER_functions.cpp 2015-02-18 19:27:00 +0000 +++ common/common_plotGERBER_functions.cpp 2015-03-01 22:25:20 +0000 @@ -559,6 +559,120 @@ } +void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ) + +{ + wxASSERT( outputFile ); + + // XXX to do: use an aperture macro to declare the pad if possible + + if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 ) + && trace_mode == FILLED ) + { + wxSize size; + DPOINT pos_dev; + + // flash the large rectangle of the center polygon + pos_dev = userToDeviceCoordinates( pos ); + if( orient == 0 || orient == 1800 ) + { + size = wxSize( corners[4].x - corners[11].x, + corners[4].y - corners[5].y ); + } + else + { + size = wxSize( corners[1].y - corners[8].y, + corners[3].x - corners[0].x ); + } + selectAperture( size, APERTURE::Rect ); + emitDcode( pos_dev, 3 ); + // flash the top small rectangle of the center polygon + if( orient == 0 || orient == 1800 ) + { + pos_dev = userToDeviceCoordinates( pos + wxPoint( 0, corners[0].y + cornerRadius / 2 )); + size = wxSize( corners[3].x - corners[0].x, + corners[1].y - corners[0].y ); + } + else + { + pos_dev = userToDeviceCoordinates( pos + wxPoint( 0, corners[3].x + cornerRadius / 2 )); + size = wxSize( corners[3].y - corners[6].y, + corners[4].x - corners[3].x ); + } + selectAperture( size, APERTURE::Rect ); + emitDcode( pos_dev, 3 ); + // flash the bottom small rectangle of the center polygon + if( orient == 0 || orient == 1800 ) + { + pos_dev = userToDeviceCoordinates( pos - wxPoint( 0, corners[0].y + cornerRadius / 2 )); + } + else + { + pos_dev = userToDeviceCoordinates( pos - wxPoint( 0, corners[3].x + cornerRadius / 2 )); + } + selectAperture( size, APERTURE::Rect ); + emitDcode( pos_dev, 3 ); + // Flash the 4 corner circles + size = wxSize( cornerRadius << 1, cornerRadius << 1 ); + selectAperture( size, APERTURE::Circle ); + if( orient == 0 || orient == 1800 ) + { + pos_dev = userToDeviceCoordinates( corners[0] + pos ); + emitDcode( pos_dev, 3 ); + pos_dev = userToDeviceCoordinates( corners[3] + pos ); + emitDcode( pos_dev, 3 ); + pos_dev = userToDeviceCoordinates( corners[6] + pos ); + emitDcode( pos_dev, 3 ); + pos_dev = userToDeviceCoordinates( corners[9] + pos ); + emitDcode( pos_dev, 3 ); + } + else + { + pos_dev = userToDeviceCoordinates( + wxPoint( corners[0].y, -corners[0].x ) + pos ); + emitDcode( pos_dev, 3 ); + pos_dev = userToDeviceCoordinates( + wxPoint( corners[3].y, -corners[3].x ) + pos ); + emitDcode( pos_dev, 3 ); + pos_dev = userToDeviceCoordinates( + wxPoint( corners[6].y, -corners[6].x ) + pos ); + emitDcode( pos_dev, 3 ); + pos_dev = userToDeviceCoordinates( + wxPoint( corners[9].y, -corners[9].x ) + pos ); + emitDcode( pos_dev, 3 ); + } + } + else + { + std::vector< wxPoint > cornerList; + + // construct the rotated polygons corner list + for( int i = 2; i < 12; i += 3 ) + { + wxPoint p( corners[i] ); + RotatePoint( &p, orient ); + p += pos; + cornerList.push_back( p ); + wxPoint center( corners[(i + 1) % 12] ); + RotatePoint( ¢er, orient ); + for( int rot = 100; rot < 900; rot += 100 ) + { + wxPoint pr( corners[i] ); + RotatePoint( &pr, orient ); + RotatePoint( &pr, center, rot ); + pr += pos; + cornerList.push_back( pr ); + } + } + // plot it + SetCurrentLineWidth( -1 ); + PlotPoly( cornerList, trace_mode == SKETCH ? NO_FILL : FILLED_SHAPE ); + } +} + + void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) === modified file 'common/common_plotHPGL_functions.cpp' --- common/common_plotHPGL_functions.cpp 2015-02-26 10:33:15 +0000 +++ common/common_plotHPGL_functions.cpp 2015-03-01 22:25:20 +0000 @@ -251,14 +251,21 @@ /** - * HPGL rectangle: fill not supported + * HPGL rectangle */ void HPGL_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) { wxASSERT( outputFile ); DPOINT p2dev = userToDeviceCoordinates( p2 ); MoveTo( p1 ); - fprintf( outputFile, "EA %.0f,%.0f;\n", p2dev.x, p2dev.y ); + if( fill == NO_FILL ) + { + fprintf( outputFile, "EA %.0f,%.0f;\n", p2dev.x, p2dev.y ); + } + else + { + fprintf( outputFile, "RA %.0f,%.0f;\n", p2dev.x, p2dev.y ); + } PenFinish(); } @@ -631,6 +638,115 @@ } } +void HPGL_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ) +{ + std::vector< wxPoint > cornerList; + + wxASSERT( outputFile ); + + for( int ii = 0; ii < 12; ii++ ) + cornerList.push_back( corners[ii] ); + + int w = KiROUND( penDiameter ); + int hw = w / 2; + + if( trace_mode != FILLED ) + { + cornerList[0].x += hw; + cornerList[0].y -= hw; + cornerList[1].x += hw; + cornerList[1].y -= hw; + cornerList[2].x -= hw; + cornerList[2].y -= hw; + cornerList[3].x -= hw; + cornerList[3].y -= hw; + cornerList[4].x -= hw; + cornerList[4].y -= hw; + cornerList[5].x -= hw; + cornerList[5].y += hw; + cornerList[6].x -= hw; + cornerList[6].y += hw; + cornerList[7].x -= hw; + cornerList[7].y += hw; + cornerList[8].x += hw; + cornerList[8].y += hw; + cornerList[9].x += hw; + cornerList[9].y += hw; + cornerList[10].x += hw; + cornerList[10].y += hw; + cornerList[11].x += hw; + cornerList[11].y -= hw; + } + + for( unsigned ii = 0; ii < cornerList.size(); ii++ ) + { + RotatePoint( &cornerList[ii], orient ); + cornerList[ii] += pos; + } + + if( trace_mode == FILLED ) + { + wxPoint cpos; + + if( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 ) + { + Rect( cornerList[10], cornerList[4], FILLED_SHAPE, w ); + Rect( cornerList[0], cornerList[2], FILLED_SHAPE, w ); + Rect( cornerList[8], cornerList[6], FILLED_SHAPE, w ); + } + else + { + // (ab)use FlashPadRect() as a generalized Rect() + wxPoint s( corners[4] - corners[10] ); + FlashPadRect( pos, wxSize( s.x, s.y ), orient, FILLED ); + // small top reactangle + cpos = wxPoint( 0, corners[1].y - cornerRadius / 2 ); + RotatePoint( &cpos, orient ); + s = corners[2] - corners[0]; + FlashPadRect( pos + cpos, wxSize( s.x, s.y ), orient, FILLED ); + // small bottom rectangle + cpos = wxPoint( 0, corners[8].y + cornerRadius / 2 ); + RotatePoint( &cpos, orient ); + s = corners[6] - corners[8]; + FlashPadRect( pos + cpos, wxSize( s.x, s.y ), orient, FILLED ); + } + // (ab)use FlashPadCircle for filled circle + cpos = corners[0]; + RotatePoint( &cpos, orient ); + FlashPadCircle( pos + cpos, cornerRadius * 2, FILLED ); + cpos = corners[3]; + RotatePoint( &cpos, orient ); + FlashPadCircle( pos + cpos, cornerRadius * 2, FILLED ); + cpos = corners[6]; + RotatePoint( &cpos, orient ); + FlashPadCircle( pos + cpos, cornerRadius * 2, FILLED ); + cpos = corners[9]; + RotatePoint( &cpos, orient ); + FlashPadCircle( pos + cpos, cornerRadius * 2, FILLED ); + } + else + { + MoveTo( cornerList[1] ); + LineTo( cornerList[2] ); + MoveTo( cornerList[4] ); + LineTo( cornerList[5] ); + MoveTo( cornerList[7] ); + LineTo( cornerList[8] ); + MoveTo( cornerList[10] ); + FinishTo( cornerList[11] ); + + Arc(cornerList[0], orient + 1800, orient + 2700, cornerRadius, + NO_FILL, w ); + Arc(cornerList[3], orient + 2700, orient + 3600, cornerRadius, + NO_FILL, w ); + Arc(cornerList[6], orient, orient + 900, cornerRadius, + NO_FILL, w ); + Arc(cornerList[9], orient + 900, orient + 1800, cornerRadius, + NO_FILL, w ); + } +} void HPGL_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) === modified file 'common/common_plotPS_functions.cpp' --- common/common_plotPS_functions.cpp 2014-10-19 20:20:16 +0000 +++ common/common_plotPS_functions.cpp 2015-03-01 22:25:20 +0000 @@ -181,6 +181,82 @@ PlotPoly( cornerList, ( trace_mode == FILLED ) ? FILLED_SHAPE : NO_FILL ); } +void PSLIKE_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ) +{ + static std::vector< wxPoint > cornerList; + cornerList.clear(); + + for( int ii = 0; ii < 12; ii++ ) + cornerList.push_back( corners[ii] ); + + // As far as I understand it line and pen width are the same thing??? + + SetCurrentLineWidth( -1 ); + int w = GetCurrentLineWidth(); + int hw = w / 2; + + cornerList[0].x += hw; + cornerList[0].y -= hw; + cornerList[1].x += hw; + cornerList[1].y -= hw; + cornerList[2].x -= hw; + cornerList[2].y -= hw; + cornerList[3].x -= hw; + cornerList[3].y -= hw; + cornerList[4].x -= hw; + cornerList[4].y -= hw; + cornerList[5].x -= hw; + cornerList[5].y += hw; + cornerList[6].x -= hw; + cornerList[6].y += hw; + cornerList[7].x -= hw; + cornerList[7].y += hw; + cornerList[8].x += hw; + cornerList[8].y += hw; + cornerList[9].x += hw; + cornerList[9].y += hw; + cornerList[10].x += hw; + cornerList[10].y += hw; + cornerList[11].x += hw; + cornerList[11].y -= hw; + + for( unsigned ii = 0; ii < cornerList.size(); ii++ ) + { + RotatePoint( &cornerList[ii], orient ); + cornerList[ii] += pos; + } + + cornerList.push_back( cornerList[0] ); + + if( trace_mode == FILLED ) + { + PlotPoly( cornerList, FILLED_SHAPE ); + } + else + { + for( unsigned ii = 1; ii < 12; ii += 3 ) + { + MoveTo( cornerList[ii] ); + FinishTo( cornerList[ii + 1] ); + } + } + + if ( w > 2 * cornerRadius ) + w = 2 * cornerRadius; + + Arc(cornerList[0], orient + 1800, orient + 2700, cornerRadius, + ( trace_mode == FILLED ) ? FILLED_SHAPE : NO_FILL, w ); + Arc(cornerList[3], orient + 2700, orient + 3600, cornerRadius, + ( trace_mode == FILLED ) ? FILLED_SHAPE : NO_FILL, w ); + Arc(cornerList[6], orient, orient + 900, cornerRadius, + ( trace_mode == FILLED ) ? FILLED_SHAPE : NO_FILL, w ); + Arc(cornerList[9], orient + 900, orient + 1800, cornerRadius, + ( trace_mode == FILLED ) ? FILLED_SHAPE : NO_FILL, w ); + + SetCurrentLineWidth( -1 ); +} void PSLIKE_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) === modified file 'common/pcb.keywords' --- common/pcb.keywords 2013-09-17 00:52:08 +0000 +++ common/pcb.keywords 2015-03-01 22:25:20 +0000 @@ -142,6 +142,7 @@ reference right rotate +roundrect scale segment segment_width === modified file 'include/pad_shapes.h' --- include/pad_shapes.h 2014-01-28 09:43:55 +0000 +++ include/pad_shapes.h 2015-03-01 22:25:20 +0000 @@ -15,7 +15,8 @@ PAD_ROUND = PAD_CIRCLE, PAD_RECT, PAD_OVAL, - PAD_TRAPEZOID + PAD_TRAPEZOID, + PAD_ROUNDRECT }; /** === modified file 'include/plot_common.h' --- include/plot_common.h 2015-02-26 10:33:15 +0000 +++ include/plot_common.h 2015-03-01 22:25:20 +0000 @@ -257,6 +257,18 @@ virtual void FlashPadRect( const wxPoint& pos, const wxSize& size, double orient, EDA_DRAW_MODE_T trace_mode ) = 0; + /** + * @param pos Position of the shape + * @param corners List of the 12 corners of the inner polygon, the arcs + * for the corners are centered on corner 0, 3, 6 and 9 + * @param cornerRadius Radius of the rounded corners + * @param orient The rotattion of the shape + * @param trace_mode FILLED or SKETCH + */ + virtual void FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ) = 0; + /** virtual function FlashPadTrapez * flash a trapezoidal pad * @param aPadPos = the position of the shape @@ -509,6 +521,9 @@ EDA_DRAW_MODE_T trace_mode ); virtual void FlashPadRect( const wxPoint& pos, const wxSize& size, double orient, EDA_DRAW_MODE_T trace_mode ); + virtual void FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ); virtual void FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ); @@ -561,6 +576,9 @@ EDA_DRAW_MODE_T trace_mode ); virtual void FlashPadRect( const wxPoint& pos, const wxSize& size, double orient, EDA_DRAW_MODE_T trace_mode ); + virtual void FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ); virtual void FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ); @@ -921,6 +939,13 @@ double orient, EDA_DRAW_MODE_T trace_mode ); /** + * Roundrect pad at the moment are *never* handled as aperture, since + * they require aperture macros + */ + virtual void FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ); + /** * Trapezoidal pad at the moment are *never* handled as aperture, since * they require aperture macros */ @@ -1049,6 +1074,9 @@ EDA_DRAW_MODE_T trace_mode ); virtual void FlashPadRect( const wxPoint& pos, const wxSize& size, double orient, EDA_DRAW_MODE_T trace_mode ); + virtual void FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12], + int cornerRadius, double orient, + EDA_DRAW_MODE_T trace_mode ); virtual void FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ); === modified file 'pcbnew/board_items_to_polygon_shape_transform.cpp' --- pcbnew/board_items_to_polygon_shape_transform.cpp 2015-02-18 19:27:00 +0000 +++ pcbnew/board_items_to_polygon_shape_transform.cpp 2015-03-01 22:25:20 +0000 @@ -605,6 +605,44 @@ aCornerBuffer.ImportFrom( shapeWithClearance ); } break; + + case PAD_ROUNDRECT: + { + wxPoint corners[12]; + int r = BuildPadPolygonRoundRect( corners, wxSize( 0, 0 ), angle ); + + // We are using ClipperLib to inflate the polygon shape, using + // arcs to connect moved segments. + ClipperLib::Path outline; + ClipperLib::Paths shapeWithClearance; + + for( int ii = 0; ii < 12; ii += 3 ) + { + corners[ii] += PadShapePos; + outline << ClipperLib::IntPoint( corners[ii].x, corners[ii].y ); + } + + ClipperLib::ClipperOffset offset_engine; + // Prepare an offset (inflate) transform, with edges connected by arcs + offset_engine.AddPath( outline, ClipperLib::jtRound, ClipperLib::etClosedPolygon ); + + // 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 + r) / 3.14 / aCircleToSegmentsCount; + + double rounding_radius = (aClearanceValue + r) * aCorrectionFactor; + offset_engine.Execute( shapeWithClearance, rounding_radius ); + + // get new outline (only one polygon is expected) + aCornerBuffer.ImportFrom( shapeWithClearance ); + } + break; } } @@ -626,6 +664,7 @@ { case PAD_CIRCLE: case PAD_OVAL: + case PAD_ROUNDRECT: TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x, aSegmentsPerCircle, aCorrectionFactor ); break; @@ -1052,6 +1091,79 @@ } break; + case PAD_ROUNDRECT: + { + std::vector <wxPoint> corners_buffer; // Polygon buffer as vector + + int dx = (aPad.GetSize().x / 2) + aThermalGap; + int dy = (aPad.GetSize().y / 2) + aThermalGap; + + // 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 r = aPad.GetRoundRectCornerRadius(); + // the center around which the round part of the thermal gap is drawn + wxPoint center = wxPoint( -(aPad.GetSize().x / 2 - r), -(aPad.GetSize().y / 2 - r) ); + + corners_buffer.push_back( wxPoint( center.x, -dy )); + + for( int i = 1; i < aCircleToSegmentsCount / 4 + 1; i++ ) + { + wxPoint corner_position = wxPoint( center.x, -dy ); + + double angle_pg = i * delta; + RotatePoint( &corner_position, center, angle_pg ); + + corners_buffer.push_back( wxPoint( corner_position.x, corner_position.y ) ); + } + + double angle = aPad.GetOrientation(); + + 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.Append( CPolyPt( cpos.x, cpos.y ) ); + } + + aCornerBuffer.CloseLastContour(); + 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++ ) + { + for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) + { + wxPoint cpos = corners_buffer[ic]; + RotatePoint( &cpos, angle ); + cpos += PadShapePos; + aCornerBuffer.Append( CPolyPt( cpos.x, cpos.y ) ); + } + + aCornerBuffer.CloseLastContour(); + angle = AddAngles( angle, 1800 ); + } + } + break; + case PAD_TRAPEZOID: { CPOLYGONS_LIST cbuffer; === modified file 'pcbnew/class_pad.cpp' --- pcbnew/class_pad.cpp 2015-02-28 17:39:05 +0000 +++ pcbnew/class_pad.cpp 2015-03-01 22:25:20 +0000 @@ -141,6 +141,14 @@ radius = 1 + KiROUND( hypot( x, y ) / 2 ); break; + case PAD_ROUNDRECT: + radius = GetRoundRectCornerRadius( m_Size ); + x = m_Size.x >> 1; + y = m_Size.y >> 1; + radius = 1 + KiROUND( EuclideanNorm( wxSize( x - radius, y - radius ))) + + radius; + break; + default: radius = 0; } @@ -180,6 +188,7 @@ break; case PAD_RECT: + case PAD_ROUNDRECT: //Use two corners and track their rotation // (utilise symmetry to avoid four points) quadrant1.x = m_Size.x/2; @@ -774,6 +783,30 @@ return true; break; + + case PAD_ROUNDRECT: + { + // First check for hit in polygon + wxPoint poly[12]; + int r = BuildPadPolygonRoundRect( poly, wxSize(0,0), 0 ); + RotatePoint( &delta, -m_Orient ); + if( TestPointInsidePolygon( poly, 12, delta )) + return true; + // Then check the rounded corners + delta = aPosition - shape_pos - poly[0]; + if( KiROUND( EuclideanNorm( delta ) ) <= r ) + return true; + delta = aPosition - shape_pos - poly[3]; + if( KiROUND( EuclideanNorm( delta ) ) <= r ) + return true; + delta = aPosition - shape_pos - poly[6]; + if( KiROUND( EuclideanNorm( delta ) ) <= r ) + return true; + delta = aPosition - shape_pos - poly[9]; + if( KiROUND( EuclideanNorm( delta ) ) <= r ) + return true; + } + break; } return false; @@ -860,6 +893,9 @@ case PAD_TRAPEZOID: return _( "Trap" ); + case PAD_ROUNDRECT: + return _( "Roundrect" ); + default: return wxT( "???" ); } === modified file 'pcbnew/class_pad.h' --- pcbnew/class_pad.h 2015-02-18 19:27:00 +0000 +++ pcbnew/class_pad.h 2015-03-01 22:25:20 +0000 @@ -292,6 +292,27 @@ void BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, double aRotation ) const; /** + * Function BuildPadPolygonRoundRect + * Has meaning only for rounded rect pads + * Build the Corner list of the polygonal center shape, + * depending on shape, extra size (clearance ...) and orientation + * @param aCoord = a buffer to fill (12 corners). + * @param aInflateValue = wxSize: the clearance or margin value. value > 0: + * inflate, < 0 deflate + * @param aRotation = full rotation of the polygon + * @return Radius of the rounded corner arcs + */ + int BuildPadPolygonRoundRect( wxPoint aCoord[12], wxSize aInflateValue, double aRotation ) const; + + /** + * Function GetRoundRectCornerRadius + * Has meaning only for rounded rect pads + * Returns the radius of the rounded corners for this pad. + * @return The radius of the rounded corners for this pad. + */ + int GetRoundRectCornerRadius() const; + + /** * Function BuildPadShapePolygon * Build the Corner list of the polygonal shape, * depending on shape, extra size (clearance ...) pad and orientation @@ -475,6 +496,18 @@ private: /** + * Private helper function BuildPadPolygonRoundRect + * Has meaning only for rounded rect pads + * Build the Corner list of the non-rotated polygonal center shape + * @param aCoord = a buffer to fill (12 corners). + * @param size Size of shape to build + * @return Radius of the rounded corner arcs + */ + int BuildPadPolygonRoundRect( wxPoint aCoord[12], wxSize size ) const; + + int GetRoundRectCornerRadius( wxSize size ) const; + + /** * Function boundingRadius * returns a calculated radius of a bounding circle for this pad. */ @@ -494,7 +527,7 @@ wxPoint m_Pos; ///< pad Position on board - PAD_SHAPE_T m_padShape; ///< Shape: PAD_CIRCLE, PAD_RECT, PAD_OVAL, PAD_TRAPEZOID + PAD_SHAPE_T m_padShape; ///< Shape: PAD_CIRCLE, PAD_RECT, PAD_OVAL, PAD_TRAPEZOID, PAD_ROUNDRECT int m_SubRatsnest; ///< variable used in rats nest computations === modified file 'pcbnew/class_pad_draw_functions.cpp' --- pcbnew/class_pad_draw_functions.cpp 2015-02-28 20:50:35 +0000 +++ pcbnew/class_pad_draw_functions.cpp 2015-03-01 22:25:20 +0000 @@ -315,9 +315,10 @@ void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) { - wxPoint coord[4]; + wxPoint coord[12]; double angle = m_Orient; int seg_width; + int r; GRSetDrawMode( aDC, aDrawInfo.m_DrawMode ); @@ -401,6 +402,80 @@ } break; + case PAD_ROUNDRECT: + r = BuildPadPolygonRoundRect( coord, aDrawInfo.m_Mask_margin, angle ); + + for( int ii = 0; ii < 12; ii++ ) + coord[ii] += shape_pos; + + if( aDrawInfo.m_ShowPadFilled ) + { + GRClosedPoly( aClipBox, aDC, 12, coord, true, 0, + aDrawInfo.m_Color, aDrawInfo.m_Color ); + GRFilledArc( aClipBox, aDC, coord[0].x, coord[0].y, + 900.0 - angle, 1800.0 - angle, r, + aDrawInfo.m_Color, aDrawInfo.m_Color ); + GRFilledArc( aClipBox, aDC, coord[3].x, coord[3].y, + 0.0 - angle, 900.0 - angle, r, + aDrawInfo.m_Color, aDrawInfo.m_Color ); + GRFilledArc( aClipBox, aDC, coord[6].x, coord[6].y, + 2700.0 - angle, 0.0 - angle, r, + aDrawInfo.m_Color, aDrawInfo.m_Color ); + GRFilledArc( aClipBox, aDC, coord[9].x, coord[9].y, + 1800.0 - angle, 2700.0 - angle, r, + aDrawInfo.m_Color, aDrawInfo.m_Color ); + } + else + { + for( int ii = 1; ii < 12; ii += 3 ) + { + GRLine( aClipBox, aDC, coord[ii].x, coord[ii].y, + coord[ii + 1].x, coord[ii + 1].y, 0, + aDrawInfo.m_Color ); + } + GRArc( aClipBox, aDC, coord[0].x, coord[0].y, + 900.0 - angle, 1800.0 - angle, r, + m_PadSketchModePenSize, aDrawInfo.m_Color ); + GRArc( aClipBox, aDC, coord[3].x, coord[3].y, + 0.0 - angle, 900.0 - angle, r, + m_PadSketchModePenSize, aDrawInfo.m_Color ); + GRArc( aClipBox, aDC, coord[6].x, coord[6].y, + 2700.0 - angle, 0.0 - angle, r, + m_PadSketchModePenSize, aDrawInfo.m_Color ); + GRArc( aClipBox, aDC, coord[9].x, coord[9].y, + 1800.0 - angle, 2700.0 - angle, r, + m_PadSketchModePenSize, aDrawInfo.m_Color ); + } + + if( aDrawInfo.m_PadClearance ) + { + r = BuildPadPolygonRoundRect( coord, wxSize( aDrawInfo.m_PadClearance, + aDrawInfo.m_PadClearance ), + angle ); + for( int ii = 0; ii < 12; ii++ ) + coord[ii] += shape_pos; + + for( int ii = 1; ii < 12; ii += 3 ) + { + GRLine( aClipBox, aDC, coord[ii].x, coord[ii].y, + coord[ii + 1].x, coord[ii + 1].y, 0, + aDrawInfo.m_Color ); + } + GRArc( aClipBox, aDC, coord[0].x, coord[0].y, + 900.0 - angle, 1800.0 - angle, r, + 0, aDrawInfo.m_Color ); + GRArc( aClipBox, aDC, coord[3].x, coord[3].y, + 0.0 - angle, 900.0 - angle, r, + 0, aDrawInfo.m_Color ); + GRArc( aClipBox, aDC, coord[6].x, coord[6].y, + 2700.0 - angle, 0.0 - angle, r, + 0, aDrawInfo.m_Color ); + GRArc( aClipBox, aDC, coord[9].x, coord[9].y, + 1800.0 - angle, 2700.0 - angle, r, + 0, aDrawInfo.m_Color ); + } + break; + default: break; } @@ -763,3 +838,150 @@ RotatePoint( &aCoord[ii], aRotation ); } } + +int D_PAD::BuildPadPolygonRoundRect( wxPoint aCoord[12], wxSize aInflateValue, + double aRotation ) const +{ + int r; + + /* + * There are three cases here: + * + * 1. aInflateValue.x == ainflateValue.y == 0 + * + * This means the actual pad polygon is build. + * + * 2. aInflateValue.x == ainflateValue.y and both > 0 + * + * This means the clearance polygon is build. + * + * 3. aInflateValue.x <= 0 && ainflateValue.y <= 0 but x and y may differ + * + * This means the paste polygon is build. + * + * Other cases are not supported (and can't happen as far as I see). + */ + + if( aInflateValue.x == 0 && aInflateValue.y == 0 ) // case 1 + { + r = BuildPadPolygonRoundRect( aCoord, m_Size ); + } + else if( aInflateValue.x > 0 ) // case 2 + { + r = BuildPadPolygonRoundRect( aCoord, m_Size ); + + // inflate the polygon and correct the corner radius + // this makes sure the distance between pad and clearance polygons + // is the same everywhere + aCoord[1].y += aInflateValue.y; + aCoord[2].y += aInflateValue.y; + aCoord[4].x += aInflateValue.x; + aCoord[5].x += aInflateValue.x; + aCoord[7].y -= aInflateValue.y; + aCoord[8].y -= aInflateValue.y; + aCoord[10].x -= aInflateValue.x; + aCoord[11].x -= aInflateValue.x; + r += aInflateValue.x; + } + else if( aInflateValue.x <= 0 && aInflateValue.y <= 0 ) // case 3 + { + // because inflation (actually shrinkage) may be asymmetrical here + // equal distance between pad and solder paste can't be done + // looks good enough anyway + r = BuildPadPolygonRoundRect( aCoord, m_Size + aInflateValue ); + } + else + { + // just in case return a valid but very small (100 nm) polygon + r = BuildPadPolygonRoundRect( aCoord, wxSize( 100, 100 ) ); + } + + // Finally rotate the polygon + + if( aRotation ) + { + for( int ii = 0; ii < 12; ii++ ) + RotatePoint( &aCoord[ii], aRotation ); + } + + return r; +} + +int D_PAD::BuildPadPolygonRoundRect( wxPoint aCoord[12], wxSize size ) const +{ + wxSize clampedSize; + wxSize halfsize; + int r; + + // clamp the minimum pad edge length to 100 nm to avoid numerical problems + clampedSize.x = size.x < 100 ? 100 : size.x; + clampedSize.y = size.y < 100 ? 100 : size.y; + + halfsize.x = clampedSize.x >> 1; + halfsize.y = clampedSize.y >> 1; + + r = GetRoundRectCornerRadius( clampedSize ); + + /* + * The unrotated pad polygon for PAD_ROUNDRECT looks like this: + * + * 1 2 + * +----+ + * 0| |3 + * 11+--+ +--+4 + * | | + * | | + * 10+--+ +--+5 + * 9| |6 + * +----+ + * 8 7 + * + * The centers of the rounded corner arcs are at 0, 3, 6 and 9 + * DO NOT CHANGE THAT, callers depend on it + * + */ + aCoord[0].x = -halfsize.x + r; + aCoord[0].y = +halfsize.y - r; + aCoord[1].x = -halfsize.x + r; + aCoord[1].y = +halfsize.y; + aCoord[2].x = +halfsize.x - r; + aCoord[2].y = +halfsize.y; + aCoord[3].x = +halfsize.x - r; + aCoord[3].y = +halfsize.y - r; + aCoord[4].x = +halfsize.x; + aCoord[4].y = +halfsize.y - r; + aCoord[5].x = +halfsize.x; + aCoord[5].y = -halfsize.y + r; + aCoord[6].x = +halfsize.x - r; + aCoord[6].y = -halfsize.y + r; + aCoord[7].x = +halfsize.x - r; + aCoord[7].y = -halfsize.y; + aCoord[8].x = -halfsize.x + r; + aCoord[8].y = -halfsize.y; + aCoord[9].x = -halfsize.x + r; + aCoord[9].y = -halfsize.y + r; + aCoord[10].x = -halfsize.x; + aCoord[10].y = -halfsize.y + r; + aCoord[11].x = -halfsize.x; + aCoord[11].y = +halfsize.y - r; + + return r; +} + +int D_PAD::GetRoundRectCornerRadius( wxSize size ) const +{ + // radius of rounded corners, fixed 25% of shorter pad edge for now + int r = size.x > size.y ? (size.y >> 2) : (size.x >> 2); + // but not more than 0.25 mm + if ( r > 250000 ) + { + r = 250000; + } + + return r; +} + +int D_PAD::GetRoundRectCornerRadius() const +{ + return GetRoundRectCornerRadius( m_Size ); +} === modified file 'pcbnew/dialogs/dialog_pad_properties.cpp' --- pcbnew/dialogs/dialog_pad_properties.cpp 2014-11-19 18:39:02 +0000 +++ pcbnew/dialogs/dialog_pad_properties.cpp 2015-03-01 22:25:20 +0000 @@ -55,7 +55,8 @@ PAD_CIRCLE, PAD_OVAL, PAD_RECT, - PAD_TRAPEZOID + PAD_TRAPEZOID, + PAD_ROUNDRECT }; @@ -512,6 +513,10 @@ case PAD_TRAPEZOID: m_PadShape->SetSelection( 3 ); break; + + case PAD_ROUNDRECT: + m_PadShape->SetSelection( 4 ); + break; } msg.Printf( wxT( "%g" ), angle ); @@ -592,6 +597,14 @@ m_ShapeOffset_X_Ctrl->Enable( true ); m_ShapeOffset_Y_Ctrl->Enable( true ); break; + + case 4: // PAD_ROUNDRECT: + m_ShapeDelta_Ctrl->Enable( false ); + m_trapDeltaDirChoice->Enable( false ); + m_ShapeSize_Y_Ctrl->Enable( true ); + m_ShapeOffset_X_Ctrl->Enable( true ); + m_ShapeOffset_Y_Ctrl->Enable( true ); + break; } transferDataToPad( m_dummyPad ); @@ -1126,6 +1139,10 @@ case PAD_TRAPEZOID: break; + case PAD_ROUNDRECT: + aPad->SetDelta( wxSize( 0, 0 ) ); + break; + default: ; } === modified file 'pcbnew/dialogs/dialog_pad_properties_base.cpp' --- pcbnew/dialogs/dialog_pad_properties_base.cpp 2015-02-18 19:27:00 +0000 +++ pcbnew/dialogs/dialog_pad_properties_base.cpp 2015-03-01 22:25:20 +0000 @@ -65,10 +65,10 @@ m_staticText45->Wrap( -1 ); fgSizerPadType->Add( m_staticText45, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP, 5 ); - wxString m_PadShapeChoices[] = { _("Circular"), _("Oval"), _("Rectangular"), _("Trapezoidal") }; + wxString m_PadShapeChoices[] = { _("Circular"), _("Oval"), _("Rectangular"), _("Trapezoidal"), _("Rounded Rectangle") }; int m_PadShapeNChoices = sizeof( m_PadShapeChoices ) / sizeof( wxString ); m_PadShape = new wxChoice( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_PadShapeNChoices, m_PadShapeChoices, 0 ); - m_PadShape->SetSelection( 0 ); + m_PadShape->SetSelection( 4 ); fgSizerPadType->Add( m_PadShape, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 ); === modified file 'pcbnew/dialogs/dialog_pad_properties_base.fbp' --- pcbnew/dialogs/dialog_pad_properties_base.fbp 2015-02-18 19:27:00 +0000 +++ pcbnew/dialogs/dialog_pad_properties_base.fbp 2015-03-01 22:25:20 +0000 @@ -271,16 +271,16 @@ <property name="border">5</property> <property name="flag">wxALL|wxEXPAND</property> <property name="proportion">3</property> - <object class="wxBoxSizer" expanded="0"> + <object class="wxBoxSizer" expanded="1"> <property name="minimum_size"></property> <property name="name">m_LeftBoxSizer</property> <property name="orient">wxVERTICAL</property> <property name="permission">none</property> - <object class="sizeritem" expanded="0"> + <object class="sizeritem" expanded="1"> <property name="border">5</property> <property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property> <property name="proportion">0</property> - <object class="wxFlexGridSizer" expanded="0"> + <object class="wxFlexGridSizer" expanded="1"> <property name="cols">2</property> <property name="flexible_direction">wxBOTH</property> <property name="growablecols">1</property> @@ -912,7 +912,7 @@ <property name="caption"></property> <property name="caption_visible">1</property> <property name="center_pane">0</property> - <property name="choices">"Circular" "Oval" "Rectangular" "Trapezoidal"</property> + <property name="choices">"Circular" "Oval" "Rectangular" "Trapezoidal" "Rounded Rectangle"</property> <property name="close_button">1</property> <property name="context_help"></property> <property name="context_menu">1</property> @@ -942,7 +942,7 @@ <property name="pin_button">1</property> <property name="pos"></property> <property name="resize">Resizable</property> - <property name="selection">0</property> + <property name="selection">4</property> <property name="show">1</property> <property name="size"></property> <property name="style"></property> === modified file 'pcbnew/drc_clearance_test_functions.cpp' --- pcbnew/drc_clearance_test_functions.cpp 2014-06-25 17:01:50 +0000 +++ pcbnew/drc_clearance_test_functions.cpp 2015-03-01 22:32:48 +0000 @@ -47,27 +47,24 @@ #include <polygon_test_point_inside.h> -/* compare 2 trapezoids (can be rectangle) and return true if distance > aDist - * i.e if for each edge of the first polygon distance from each edge of the other polygon - * is >= aDist - */ -bool trapezoid2trapezoidDRC( wxPoint aTref[4], wxPoint aTcompare[4], int aDist ) +bool convex2convexDRC( wxPoint* aTref, int aTrefCount, + wxPoint* aTcompare, int aTcompareCount, int aDist ) { /* Test if one polygon is contained in the other and thus the polygon overlap. - * This case is not covered by the following check if one polygond is + * This case is not covered by the following check if one polygon is * completely contained in the other (because edges don't intersect)! */ - if( TestPointInsidePolygon( aTref, 4, aTcompare[0] ) ) + if( TestPointInsidePolygon( aTref, aTrefCount, aTcompare[0] ) ) return false; - if( TestPointInsidePolygon( aTcompare, 4, aTref[0] ) ) + if( TestPointInsidePolygon( aTcompare, aTcompareCount, aTref[0] ) ) return false; int ii, jj, kk, ll; - for( ii = 0, jj = 3; ii<4; jj = ii, ii++ ) // for all edges in aTref + for( ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edges in aTref { - for( kk = 0, ll = 3; kk < 4; ll = kk, kk++ ) // for all edges in aTcompare + for( kk = 0, ll = aTcompareCount - 1; kk < aTcompareCount; ll = kk, kk++ ) // for all edges in aTcompare { double d; int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x, @@ -87,6 +84,14 @@ return true; } +/* compare 2 trapezoids (can be rectangle) and return true if distance > aDist + * i.e if for each edge of the first polygon distance from each edge of the other polygon + * is >= aDist + */ +bool trapezoid2trapezoidDRC( wxPoint aTref[4], wxPoint aTcompare[4], int aDist ) +{ + return convex2convexDRC( aTref, 4, aTcompare, 4, aDist ); +} /* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist */ @@ -121,28 +126,34 @@ } +bool convex2pointDRC( wxPoint* aTref, int aTrefCount, wxPoint aPcompare, int aDist ) +{ + /* Test if aPcompare point is contained in the polygon. + * This case is not covered by the following check if this point is inside the polygon + */ + if( TestPointInsidePolygon( aTref, aTrefCount, aPcompare ) ) + { + return false; + } + + // Test distance between aPcompare and each segment of the polygon: + for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edge in polygon + { + if( TestSegmentHit( aPcompare, aTref[ii], aTref[jj], aDist ) ) + return false; + } + + return true; +} + + /* compare a trapezoid to a point and return true if distance > aDist * do not use this function for horizontal or vertical rectangles * because there is a faster an easier way to compare the distance */ bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist ) { - /* Test if aPcompare point is contained in the polygon. - * This case is not covered by the following check if this point is inside the polygon - */ - if( TestPointInsidePolygon( aTref, 4, aPcompare ) ) - { - return false; - } - - // Test distance between aPcompare and each segment of the polygon: - for( int ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edge in polygon - { - if( TestSegmentHit( aTref[ii], aTref[jj], aPcompare, aDist ) ) - return false; - } - - return true; + return convex2pointDRC( aTref, 4, aPcompare, aDist ); } @@ -618,10 +629,16 @@ swap_pads = false; // swap pads to make comparisons easier - // priority is aRefPad = ROUND then OVAL then RECT then other + // priority is aRefPad = ROUND then OVAL then RECT then ROUNDRECT then other + // possible combos: + // C C,O,R,RR,T + // O O,R,RR,T + // R R,RR,T + // RR RR,T + // T T if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_CIRCLE ) { - // pad ref shape is here oval, rect or trapezoid + // pad ref shape is here oval, rect, roundrect or trapezoid switch( aPad->GetShape() ) { case PAD_CIRCLE: @@ -637,6 +654,11 @@ swap_pads = true; break; + case PAD_ROUNDRECT: + if( aRefPad->GetShape() != PAD_RECT ) + swap_pads = true; + break; + default: break; } @@ -650,8 +672,8 @@ /* Because pad exchange, aRefPad shape is PAD_CIRCLE or PAD_OVAL, * if one of the 2 pads was a PAD_CIRCLE or PAD_OVAL. - * Therefore, if aRefPad is a PAD_RECT or a PAD_TRAPEZOID, - * aPad is also a PAD_RECT or a PAD_TRAPEZOID + * Therefore, if aRefPad is a PAD_RECT, PAD_ROUNDRECT or a PAD_TRAPEZOID, + * aPad is also a PAD_RECT, PAD_ROUNDRECT or a PAD_TRAPEZOID */ bool diag = true; @@ -708,6 +730,7 @@ { // Use the trapezoid2trapezoidDRC which also compare 2 rectangles with any orientation wxPoint polyref[4]; // Shape of aRefPad wxPoint polycompare[4]; // Shape of aPad + aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); @@ -720,6 +743,48 @@ diag = false; } } + else if( aPad->GetShape() == PAD_ROUNDRECT ) + { + wxPoint polyref[4]; // Shape of aRefPad + wxPoint polycompare[12]; // inner shape of aPad + wxPoint polyconvex[8]; // convex inner shape of aPad + + aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); + int r = aPad->BuildPadPolygonRoundRect( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); + + // Move aPad shape to relativePadPos + for( int ii = 0; ii < 12; ii++ ) + polycompare[ii] += relativePadPos; + + // build a convex inner shape for the ROUNDRECT pad + // by leaving out the center points of the corner arcs + polyconvex[0] = polycompare[1]; + polyconvex[1] = polycompare[2]; + polyconvex[2] = polycompare[4]; + polyconvex[3] = polycompare[5]; + polyconvex[4] = polycompare[7]; + polyconvex[5] = polycompare[8]; + polyconvex[6] = polycompare[10]; + polyconvex[7] = polycompare[11]; + + // And now test polygons: + if( !convex2convexDRC( polyref, 4, polyconvex, 8, dist_min ) ) + diag = false; + + // if not already failed, check the corner circles + if( diag ) + { + for( int ii = 0; ii < 12; ii += 3 ) + { + if( !trapezoid2pointDRC( polyref, polycompare[ii], + dist_min + r ) ) + { + diag = false; + break; + } + } + } + } else if( aPad->GetShape() == PAD_TRAPEZOID ) { wxPoint polyref[4]; // Shape of aRefPad @@ -745,6 +810,144 @@ } break; + case PAD_ROUNDRECT: + if( aPad->GetShape() == PAD_ROUNDRECT ) + { + wxPoint polyref[12]; // inner shape of aRefPad + wxPoint polycompare[12]; // inner shape of aPad + wxPoint polyrefconvex[8]; // convex inner shape of aRefPad + wxPoint polycompconvex[8]; // convex inner shape of aPad + + int rref = aRefPad->BuildPadPolygonRoundRect( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); + int rcomp = aPad->BuildPadPolygonRoundRect( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); + + // Move aPad shape to relativePadPos + for( int ii = 0; ii < 12; ii++ ) + polycompare[ii] += relativePadPos; + + // build a convex inner shape for aRefPad + // by leaving out the center points of the corner arcs + polyrefconvex[0] = polyref[1]; + polyrefconvex[1] = polyref[2]; + polyrefconvex[2] = polyref[4]; + polyrefconvex[3] = polyref[5]; + polyrefconvex[4] = polyref[7]; + polyrefconvex[5] = polyref[8]; + polyrefconvex[6] = polyref[10]; + polyrefconvex[7] = polyref[11]; + + // build a convex inner shape for aPad + // by leaving out the center points of the corner arcs + polycompconvex[0] = polycompare[1]; + polycompconvex[1] = polycompare[2]; + polycompconvex[2] = polycompare[4]; + polycompconvex[3] = polycompare[5]; + polycompconvex[4] = polycompare[7]; + polycompconvex[5] = polycompare[8]; + polycompconvex[6] = polycompare[10]; + polycompconvex[7] = polycompare[11]; + + // And now test inner polygons: + if( !convex2convexDRC( polyrefconvex, 8, polycompconvex, 8, dist_min ) ) + diag = false; + + // if not already failed test ref corner arcs against aPad + if( diag ) + { + for( int ii = 0; ii < 12; ii += 3 ) + { + if( !convex2pointDRC( polycompconvex, 8, polyref[ii], + dist_min + rref ) ) + { + diag = false; + break; + } + } + } + + // if not already failed test aPad corner arcs against aRefPad + if( diag ) + { + for( int ii = 0; ii < 12; ii += 3 ) + { + if( !convex2pointDRC( polyrefconvex, 8, polycompare[ii], + dist_min + rcomp ) ) + { + diag = false; + break; + } + } + } + + // finally test corner arcs against each other if still not + // failed + if( diag ) + { + for( int ii = 0; ii < 12 && diag; ii += 3 ) + { + for( int jj = 0; jj < 12; jj += 3 ) + { + if( ( KiROUND( EuclideanNorm( polyref[ii] - polycompare[jj] ) ) + - rref - rcomp ) < dist_min ) + { + diag = false; + break; + } + } + } + } + } + else if( aPad->GetShape() == PAD_TRAPEZOID ) + { + wxPoint polyref[12]; // inner shape of aRefPad + wxPoint polycompare[4]; // shape of aPad + wxPoint polyconvex[8]; // convex inner shape of aRefPad + + int r = aRefPad->BuildPadPolygonRoundRect( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); + aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); + + // Move aPad shape to relativePadPos + for( int ii = 0; ii < 4; ii++ ) + polycompare[ii] += relativePadPos; + + // build a convex inner shape for the ROUNDRECT pad + // by leaving out the center points of the corner arcs + polyconvex[0] = polyref[1]; + polyconvex[1] = polyref[2]; + polyconvex[2] = polyref[4]; + polyconvex[3] = polyref[5]; + polyconvex[4] = polyref[7]; + polyconvex[5] = polyref[8]; + polyconvex[6] = polyref[10]; + polyconvex[7] = polyref[11]; + + // And now test polygons: + if( !convex2convexDRC( polycompare, 4, polyconvex, 8, dist_min ) ) + diag = false; + + // if not already failed, check the corner circles + if( diag ) + { + for( int ii = 0; ii < 12; ii += 3 ) + { + if( !trapezoid2pointDRC( polycompare, polyref[ii], + dist_min + r ) ) + { + diag = false; + break; + } + } + } + } + else + { + // Should not occur + wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad ref ROUNDRECT @ %d, %d to pad shape %d @ %d, %d"), + aRefPad->GetPosition().x, aRefPad->GetPosition().y, + aPad->GetShape(), aPad->GetPosition().x, aPad->GetPosition().y ); + } + break; + case PAD_OVAL: /* an oval pad is like a track segment */ { /* Create a track segment with same dimensions as the oval aRefPad @@ -828,6 +1031,7 @@ wxPoint startPoint, endPoint; int seuil; int deltay; + int r; int segmHalfWidth = aSegmentWidth / 2; @@ -992,6 +1196,66 @@ break; + case PAD_ROUNDRECT: + /* 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; + + r = aPad->GetRoundRectCornerRadius(); + + /* test des 4 cercles ( surface d'solation autour des sommets */ + /* test du coin sup. gauche du pad */ + startPoint.x = m_padToTestPos.x - padHalfsize.x + r; + startPoint.y = m_padToTestPos.y - padHalfsize.y + r; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, seuil + r, m_segmLength ) ) + return false; + + /* test du coin sup. droit du pad */ + startPoint.x = m_padToTestPos.x + padHalfsize.x - r; + startPoint.y = m_padToTestPos.y - padHalfsize.y + r; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, seuil + r, m_segmLength ) ) + return false; + + /* test du coin inf. gauche du pad */ + startPoint.x = m_padToTestPos.x - padHalfsize.x + r; + startPoint.y = m_padToTestPos.y + padHalfsize.y - r; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, seuil + r, m_segmLength ) ) + return false; + + /* test du coin inf. droit du pad */ + startPoint.x = m_padToTestPos.x + padHalfsize.x - r; + startPoint.y = m_padToTestPos.y + padHalfsize.y - r; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, seuil + r, m_segmLength ) ) + return false; + + break; + case PAD_TRAPEZOID: { wxPoint poly[4]; === modified file 'pcbnew/exporters/export_gencad.cpp' --- pcbnew/exporters/export_gencad.cpp 2015-02-22 14:43:44 +0000 +++ pcbnew/exporters/export_gencad.cpp 2015-03-01 22:25:20 +0000 @@ -553,6 +553,13 @@ // XXX TO BE IMPLEMENTED! and I don't know if it could be actually imported by something break; + + case PAD_ROUNDRECT: + fprintf( aFile, " POLYGON %g\n", + pad->GetDrillSize().x / SCALE_FACTOR ); + + // XXX TO BE IMPLEMENTED! But how to test it? + break; } } === modified file 'pcbnew/exporters/export_vrml.cpp' --- pcbnew/exporters/export_vrml.cpp 2015-02-18 19:27:00 +0000 +++ pcbnew/exporters/export_vrml.cpp 2015-03-01 22:25:20 +0000 @@ -1095,6 +1095,53 @@ break; } + case PAD_ROUNDRECT: + { + // integer coordinates of pad center polygon (already rotated) + wxPoint coordsi[12]; + aPad->BuildPadPolygonRoundRect( coordsi, wxSize(0,0), + aPad->GetOrientation() ); + + // double coordinates for VRML world + VECTOR2D coordsd[12]; + for( int i = 0; i < 12; i++ ) + { + coordsi[i] += aPad->ShapePos(); + coordsd[i] = VECTOR2D( coordsi[i].x * aModel.scale, + coordsi[i].y * aModel.scale ); + } + + int lines = aTinLayer->NewContour(); + + if( lines < 0 ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + + for( int i = 1; i < 12; i += 3 ) + { + // add one of the straight edges + if( !aTinLayer->AddVertex( lines, coordsd[i].x, -coordsd[i].y ) ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + if( !aTinLayer->AddVertex( lines, coordsd[i + 1].x, -coordsd[i + 1].y ) ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + // add the quarter circle to the next straight edge + // tried to use addArc() but it always looked weird + for( int j = 1; j < 9; j++ ) + { + VECTOR2D rp( coordsd[i + 1].x, coordsd[i + 1].y ); + RotatePoint( &rp.x, &rp.y, + coordsd[(i + 2) % 12].x, coordsd[(i + 2) % 12].y, + 100 * j ); + if( !aTinLayer->AddVertex( lines, rp.x, -rp.y ) ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + } + } + + if( !aTinLayer->EnsureWinding( lines, false ) ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + + break; + } + default: break; } === modified file 'pcbnew/exporters/gen_modules_placefile.cpp' --- pcbnew/exporters/gen_modules_placefile.cpp 2015-02-23 13:03:20 +0000 +++ pcbnew/exporters/gen_modules_placefile.cpp 2015-03-01 22:25:20 +0000 @@ -707,7 +707,7 @@ (pad->GetOrientation() - Module->GetOrientation()) / 10.0 ); fputs( line, rptfile ); - static const char* shape_name[6] = { "???", "Circ", "Rect", "Oval", "Trap", "Spec" }; + static const char* shape_name[6] = { "Circ", "Rect", "Oval", "Trap", "Rrec", "Spec" }; sprintf( line, "Shape %s\n", shape_name[pad->GetShape()] ); fputs( line, rptfile ); === modified file 'pcbnew/kicad_plugin.cpp' --- pcbnew/kicad_plugin.cpp 2015-02-22 21:25:29 +0000 +++ pcbnew/kicad_plugin.cpp 2015-03-01 22:25:20 +0000 @@ -1275,6 +1275,7 @@ case PAD_RECT: shape = "rect"; break; case PAD_OVAL: shape = "oval"; break; case PAD_TRAPEZOID: shape = "trapezoid"; break; + case PAD_ROUNDRECT: shape = "roundrect"; break; default: THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) ); === modified file 'pcbnew/pcb_painter.cpp' --- pcbnew/pcb_painter.cpp 2015-02-18 19:27:00 +0000 +++ pcbnew/pcb_painter.cpp 2015-03-01 22:25:20 +0000 @@ -650,6 +650,44 @@ m_gal->DrawRectangle( VECTOR2D( -size.x, -size.y ), VECTOR2D( size.x, size.y ) ); break; + case PAD_ROUNDRECT: + { + std::deque<VECTOR2D> pointList; + wxPoint corners[12]; + + VECTOR2D padSize = VECTOR2D( aPad->GetSize().x, aPad->GetSize().y ) / 2; + VECTOR2D deltaPadSize = size - padSize; // = solder[Paste/Mask]Margin or 0 + + int r = aPad->BuildPadPolygonRoundRect( corners, wxSize( deltaPadSize.x, deltaPadSize.y ), 0.0 ); + + if( m_pcbSettings.m_sketchMode[ITEM_GAL_LAYER( PADS_VISIBLE )] ) + { + // Outline mode + m_gal->DrawLine( VECTOR2D( corners[1] ), VECTOR2D( corners[2] ) ); + m_gal->DrawLine( VECTOR2D( corners[4] ), VECTOR2D( corners[5] ) ); + m_gal->DrawLine( VECTOR2D( corners[7] ), VECTOR2D( corners[8] ) ); + m_gal->DrawLine( VECTOR2D( corners[10] ), VECTOR2D( corners[11] ) ); + m_gal->DrawArc( VECTOR2D( corners[0] ), r, M_PI / 2, M_PI ); + m_gal->DrawArc( VECTOR2D( corners[3] ), r, 0, M_PI / 2 ); + m_gal->DrawArc( VECTOR2D( corners[6] ), r, 3 * M_PI / 2, 2 * M_PI ); + m_gal->DrawArc( VECTOR2D( corners[9] ), r, M_PI, 3 * M_PI / 2 ); + } + else + { + // Filled mode + for( int ii = 0; ii < 12; ii++ ) + { + pointList.push_back( VECTOR2D( corners[ii] ) ); + } + m_gal->DrawPolygon( pointList ); + m_gal->DrawArc( VECTOR2D( corners[0] ), r, M_PI / 2, M_PI ); + m_gal->DrawArc( VECTOR2D( corners[3] ), r, 0, M_PI / 2 ); + m_gal->DrawArc( VECTOR2D( corners[6] ), r, 3 * M_PI / 2, 2 * M_PI ); + m_gal->DrawArc( VECTOR2D( corners[9] ), r, M_PI, 3 * M_PI / 2 ); + } + break; + } + case PAD_TRAPEZOID: { std::deque<VECTOR2D> pointList; === modified file 'pcbnew/pcb_parser.cpp' --- pcbnew/pcb_parser.cpp 2015-02-22 21:25:29 +0000 +++ pcbnew/pcb_parser.cpp 2015-03-01 22:25:20 +0000 @@ -2213,8 +2213,12 @@ pad->SetShape( PAD_TRAPEZOID ); break; + case T_roundrect: + pad->SetShape( PAD_ROUNDRECT ); + break; + default: - Expecting( "circle, rectangle, oval, or trapezoid" ); + Expecting( "circle, rectangle, roundrect, oval, or trapezoid" ); } for( token = NextTok(); token != T_RIGHT; token = NextTok() ) === modified file 'pcbnew/plot_board_layers.cpp' --- pcbnew/plot_board_layers.cpp 2015-02-18 19:27:00 +0000 +++ pcbnew/plot_board_layers.cpp 2015-03-01 22:25:20 +0000 @@ -380,6 +380,7 @@ // Fall through: case PAD_TRAPEZOID: case PAD_RECT: + case PAD_ROUNDRECT: default: itemplotter.PlotPad( pad, color, plotMode ); break; === modified file 'pcbnew/plot_brditems_plotter.cpp' --- pcbnew/plot_brditems_plotter.cpp 2015-02-18 19:27:00 +0000 +++ pcbnew/plot_brditems_plotter.cpp 2015-03-01 22:25:20 +0000 @@ -91,6 +91,15 @@ } break; + case PAD_ROUNDRECT: + { + wxPoint coord[12]; + int r = aPad->BuildPadPolygonRoundRect( coord, wxSize(0,0), 0 ); + m_plotter->FlashPadRoundRect( shape_pos, coord, r, + aPad->GetOrientation(), aPlotMode ); + } + break; + case PAD_RECT: default: m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), === modified file 'pcbnew/router/pns_router.cpp' --- pcbnew/router/pns_router.cpp 2014-11-27 10:51:16 +0000 +++ pcbnew/router/pns_router.cpp 2015-03-01 22:25:20 +0000 @@ -205,6 +205,11 @@ solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) ); break; + case PAD_ROUNDRECT: + // FIXME: not optimal and the optical effect is confusing + solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) ); + break; + default: TRACEn( 0, "unsupported pad shape" ); delete solid; === modified file 'pcbnew/specctra_export.cpp' --- pcbnew/specctra_export.cpp 2015-02-17 18:47:21 +0000 +++ pcbnew/specctra_export.cpp 2015-03-01 22:25:20 +0000 @@ -512,6 +512,56 @@ } break; + case PAD_ROUNDRECT: + { + wxPoint coordsi[12]; + POINT coordsd[12]; + + aPad->BuildPadPolygonRoundRect( coordsi, wxSize( 0, 0 ), 0 ); + + for( int i = 0; i < 12; i++ ) + { + coordsd[i].x = scale( coordsi[i].x ); + coordsd[i].y = scale( coordsi[i].y ); + coordsd[i] += dsnOffset; + } + + for( int ndx = 0; ndx < reportedLayers; ++ndx ) + { + SHAPE* shape = new SHAPE( padstack ); + + padstack->Append( shape ); + + PATH* polygon = new PATH( shape, T_polygon ); + + shape->SetShape( polygon ); + + polygon->SetLayerId( layerName[ndx] ); + + for( int i = 1; i < 12; i += 3 ) + { + polygon->AppendPoint( coordsd[i] ); + polygon->AppendPoint( coordsd[i + 1] ); + + for( int j = 1; j < 9; j++ ) + { + POINT p( coordsd[i + 1] ); + RotatePoint( &p.x, &p.y, coordsd[(i + 2) % 12].x, + coordsd[(i + 2) % 12].y, 100 * j ); + polygon->AppendPoint( p ); + } + } + } + + snprintf( name, sizeof(name), "RRect%sPad_%.6gx%.6g_um", + uniqifier.c_str(), IU2um( aPad->GetSize().x ), + IU2um( aPad->GetSize().y ) ); + name[ sizeof(name)-1 ] = 0; + + padstack->SetPadstackId( name ); + } + break; + case PAD_OVAL: { double dx = scale( aPad->GetSize().x ) / 2.0; _______________________________________________ Mailing list: https://launchpad.net/~kicad-developers Post to : kicad-developers@lists.launchpad.net Unsubscribe : https://launchpad.net/~kicad-developers More help : https://help.launchpad.net/ListHelp