kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #22542
Re: [RFC PATCH] Rounded rectangle pads
Hello Clemens,
Clemens Koller <cko@xxxxxxxxx> writes:
> Hi, Mathias!
>
> On 2016-01-12 00:06, Mathias Grimmberger wrote:
>> I refreshed a part of my patch for rounded rectangle pads (minus the
>> plotting stuff, OpenGL canvas, stuff I probably forgot) so everybody
>> interested can have a look and play a bit with it, see below.
>
> Unfortunately, your patch doesn't apply cleanly [1]. And it seems
> it's not trivial merging /pcbnew/drc_clearance_test_functions.cpp
> manually.
Hmm, Kicad moves fast these days...
> Can you rebase your patch, please?
Please find attached a patch done against rev 6455, I hope I put it all
together correctly, especially the DRC clearance stuff.
I also added the plot code for Gerber output.
MGri
=== modified file 'common/common_plotDXF_functions.cpp'
--- common/common_plotDXF_functions.cpp 2015-12-15 20:21:25 +0000
+++ common/common_plotDXF_functions.cpp 2016-01-12 18:41:34 +0000
@@ -617,6 +617,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 )
+{
+ // FIXME: implement
+}
/**
* DXF trapezoidal pad: only sketch mode is supported
=== modified file 'common/common_plotGERBER_functions.cpp'
--- common/common_plotGERBER_functions.cpp 2015-06-26 13:41:56 +0000
+++ common/common_plotGERBER_functions.cpp 2016-01-12 19:51:21 +0000
@@ -559,6 +559,118 @@
}
}
+void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12],
+ int cornerRadius, double orient,
+ EDA_DRAW_MODE_T trace_mode )
+
+{
+ wxASSERT( outputFile );
+
+ // TODO: 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-06-26 13:41:56 +0000
+++ common/common_plotHPGL_functions.cpp 2016-01-12 18:41:34 +0000
@@ -633,6 +633,12 @@
}
}
+void HPGL_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12],
+ int cornerRadius, double orient,
+ EDA_DRAW_MODE_T trace_mode )
+{
+ // FIXME: implement
+}
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 2015-06-26 13:41:56 +0000
+++ common/common_plotPS_functions.cpp 2016-01-12 18:41:34 +0000
@@ -188,6 +188,12 @@
GetCurrentLineWidth() );
}
+void PSLIKE_PLOTTER::FlashPadRoundRect( const wxPoint& pos, const wxPoint corners[12],
+ int cornerRadius, double orient,
+ EDA_DRAW_MODE_T trace_mode )
+{
+ // FIXME: implement
+}
void PSLIKE_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners,
double aPadOrient, EDA_DRAW_MODE_T aTraceMode )
=== modified file 'common/pcb.keywords'
--- common/pcb.keywords 2013-09-17 00:52:08 +0000
+++ common/pcb.keywords 2016-01-12 18:41:34 +0000
@@ -142,6 +142,7 @@
reference
right
rotate
+roundrect
scale
segment
segment_width
=== modified file 'include/pad_shapes.h'
--- include/pad_shapes.h 2016-01-12 16:33:33 +0000
+++ include/pad_shapes.h 2016-01-12 18:41:34 +0000
@@ -37,7 +37,9 @@
PAD_SHAPE_OVAL,
PAD_OVAL = PAD_SHAPE_OVAL,
PAD_SHAPE_TRAPEZOID,
- PAD_TRAPEZOID = PAD_SHAPE_TRAPEZOID
+ PAD_TRAPEZOID = PAD_SHAPE_TRAPEZOID,
+ PAD_SHAPE_ROUNDRECT,
+ PAD_ROUNDRECT = PAD_SHAPE_ROUNDRECT
};
/**
=== modified file 'include/plot_common.h'
--- include/plot_common.h 2015-05-21 09:04:47 +0000
+++ include/plot_common.h 2016-01-12 18:41:34 +0000
@@ -280,6 +280,18 @@
virtual void FlashPadRect( const wxPoint& aPadPos, const wxSize& aSize,
double aPadOrient, EDA_DRAW_MODE_T aTraceMode ) = 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
@@ -534,6 +546,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 );
@@ -586,6 +601,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 );
@@ -948,6 +966,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
*/
@@ -1069,6 +1094,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-12-15 20:21:25 +0000
+++ pcbnew/board_items_to_polygon_shape_transform.cpp 2016-01-12 18:41:34 +0000
@@ -621,6 +621,29 @@
aCornerBuffer.Append( outline );
}
break;
+
+ case PAD_SHAPE_ROUNDRECT:
+ {
+ wxPoint corners[12];
+ int r = BuildPadPolygonRoundRect( corners, wxSize( 0, 0 ), angle );
+
+ SHAPE_POLY_SET outline;
+
+ outline.NewOutline();
+
+ // add only the center rectangle
+ for( int ii = 0; ii < 12; ii += 3 )
+ {
+ corners[ii] += PadShapePos;
+ outline.Append( corners[ii].x, corners[ii].y );
+ }
+
+ // (ab)use poly inflation to create a roundrect from the rectangle
+ outline.Inflate( r, 36 );
+
+ aCornerBuffer.Append( outline );
+ }
+ break;
}
}
@@ -642,6 +665,7 @@
{
case PAD_SHAPE_CIRCLE:
case PAD_SHAPE_OVAL:
+ case PAD_SHAPE_ROUNDRECT:
TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x,
aSegmentsPerCircle, aCorrectionFactor );
break;
@@ -1072,6 +1096,77 @@
}
break;
+ case PAD_SHAPE_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 ) );
+ }
+
+ angle = AddAngles( angle, 1800 ); // this is calculate hole 3
+ }
+
+ // Create holes, these are 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 ) );
+ }
+
+ angle = AddAngles( angle, 1800 );
+ }
+ }
+ break;
+
case PAD_SHAPE_TRAPEZOID:
{
SHAPE_POLY_SET antipad; // The full antipad area
=== modified file 'pcbnew/class_pad.cpp'
--- pcbnew/class_pad.cpp 2015-12-27 15:51:13 +0000
+++ pcbnew/class_pad.cpp 2016-01-12 18:41:34 +0000
@@ -142,6 +142,14 @@
radius = 1 + KiROUND( hypot( x, y ) / 2 );
break;
+ case PAD_SHAPE_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_SHAPE_RECT:
+ case PAD_SHAPE_ROUNDRECT:
//Use two corners and track their rotation
// (utilise symmetry to avoid four points)
quadrant1.x = m_Size.x/2;
@@ -768,6 +777,30 @@
return true;
break;
+
+ case PAD_SHAPE_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;
@@ -854,6 +887,9 @@
case PAD_SHAPE_TRAPEZOID:
return _( "Trap" );
+ case PAD_SHAPE_ROUNDRECT:
+ return _( "Roundrect" );
+
default:
return wxT( "???" );
}
=== modified file 'pcbnew/class_pad.h'
--- pcbnew/class_pad.h 2015-11-13 11:32:42 +0000
+++ pcbnew/class_pad.h 2016-01-12 18:41:34 +0000
@@ -324,6 +324,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
@@ -512,6 +533,25 @@
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;
+
+ /**
+ * Private helper function GetRoundRectCornerRadius
+ * Has meaning only for rounded rect pads
+ * Returns the radius of the rounded corners for this pad.
+ * @param size Size of the pad.
+ * @return The radius of the rounded corners for this pad.
+ */
+ int GetRoundRectCornerRadius( wxSize size ) const;
+
+ /**
* Function boundingRadius
* returns a calculated radius of a bounding circle for this pad.
*/
@@ -531,7 +571,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-12-20 14:09:06 +0000
+++ pcbnew/class_pad_draw_functions.cpp 2016-01-12 18:41:34 +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_SHAPE_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;
}
@@ -761,3 +836,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 2015-12-27 15:51:13 +0000
+++ pcbnew/dialogs/dialog_pad_properties.cpp 2016-01-12 18:41:34 +0000
@@ -55,7 +55,8 @@
PAD_SHAPE_CIRCLE,
PAD_SHAPE_OVAL,
PAD_SHAPE_RECT,
- PAD_SHAPE_TRAPEZOID
+ PAD_SHAPE_TRAPEZOID,
+ PAD_SHAPE_ROUNDRECT
};
@@ -510,6 +511,10 @@
case PAD_SHAPE_TRAPEZOID:
m_PadShape->SetSelection( 3 );
break;
+
+ case PAD_SHAPE_ROUNDRECT:
+ m_PadShape->SetSelection( 4 );
+ break;
}
msg.Printf( wxT( "%g" ), angle );
@@ -590,6 +595,14 @@
m_ShapeOffset_X_Ctrl->Enable( true );
m_ShapeOffset_Y_Ctrl->Enable( true );
break;
+
+ case 4: // PAD_SHAPE_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 );
@@ -1136,6 +1149,10 @@
case PAD_SHAPE_TRAPEZOID:
break;
+ case PAD_SHAPE_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-09-11 23:13:54 +0000
+++ pcbnew/dialogs/dialog_pad_properties_base.cpp 2016-01-12 18:44:35 +0000
@@ -65,7 +65,7 @@
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 );
=== modified file 'pcbnew/dialogs/dialog_pad_properties_base.fbp'
--- pcbnew/dialogs/dialog_pad_properties_base.fbp 2015-09-11 23:13:54 +0000
+++ pcbnew/dialogs/dialog_pad_properties_base.fbp 2016-01-12 18:41:34 +0000
@@ -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>
=== modified file 'pcbnew/drc_clearance_test_functions.cpp'
--- pcbnew/drc_clearance_test_functions.cpp 2016-01-09 21:07:52 +0000
+++ pcbnew/drc_clearance_test_functions.cpp 2016-01-12 19:39:21 +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
* 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
*/
@@ -120,31 +125,35 @@
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 )
+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, 4, aPcompare ) )
+ if( TestPointInsidePolygon( aTref, aTrefCount, 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
+ for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edge in polygon
{
- if( TestSegmentHit( aTref[ii], aTref[jj], aPcompare, aDist ) )
+ 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 )
+{
+ return convex2pointDRC( aTref, 4, aPcompare, aDist );
+}
+
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
{
@@ -583,6 +592,7 @@
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
{
int dist;
+ double pad_angle;
// Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
int dist_min = aRefPad->GetClearance( aPad );
@@ -607,10 +617,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_SHAPE_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_SHAPE_CIRCLE:
@@ -626,6 +642,11 @@
swap_pads = true;
break;
+ case PAD_SHAPE_ROUNDRECT:
+ if( aRefPad->GetShape() != PAD_SHAPE_RECT )
+ swap_pads = true;
+ break;
+
default:
break;
}
@@ -639,8 +660,8 @@
/* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
* if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
- * Therefore, if aRefPad is a PAD_SHAPE_SHAPE_RECT or a PAD_SHAPE_TRAPEZOID,
- * aPad is also a PAD_SHAPE_RECT or a PAD_SHAPE_TRAPEZOID
+ * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
+ * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
*/
bool diag = true;
@@ -660,6 +681,261 @@
diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
break;
+ case PAD_SHAPE_RECT:
+ // pad_angle = pad orient relative to the aRefPad orient
+ pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
+ NORMALIZE_ANGLE_POS( pad_angle );
+
+ if( aPad->GetShape() == PAD_SHAPE_RECT )
+ {
+ wxSize size = aPad->GetSize();
+
+ // The trivial case is if both rects are rotated by multiple of 90 deg
+ // Most of time this is the case, and the test is fast
+ if( ( (aRefPad->GetOrientation() == 0) || (aRefPad->GetOrientation() == 900)
+ || (aRefPad->GetOrientation() == 1800) || (aRefPad->GetOrientation() == 2700) )
+ && ( (aPad->GetOrientation() == 0) || (aPad->GetOrientation() == 900) || (aPad->GetOrientation() == 1800)
+ || (aPad->GetOrientation() == 2700) ) )
+ {
+ if( (pad_angle == 900) || (pad_angle == 2700) )
+ {
+ std::swap( size.x, size.y );
+ }
+
+ // Test DRC:
+ diag = false;
+ RotatePoint( &relativePadPos, aRefPad->GetOrientation() );
+ relativePadPos.x = std::abs( relativePadPos.x );
+ relativePadPos.y = std::abs( relativePadPos.y );
+
+ if( ( relativePadPos.x - ( (size.x + aRefPad->GetSize().x) / 2 ) ) >= dist_min )
+ diag = true;
+
+ if( ( relativePadPos.y - ( (size.y + aRefPad->GetSize().y) / 2 ) ) >= dist_min )
+ diag = true;
+ }
+ else // at least one pad has any other orient. Test is more tricky
+ { // 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() );
+
+ // Move aPad shape to relativePadPos
+ for( int ii = 0; ii < 4; ii++ )
+ polycompare[ii] += relativePadPos;
+
+ // And now test polygons:
+ if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) )
+ diag = false;
+ }
+ }
+ else if( aPad->GetShape() == PAD_SHAPE_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_SHAPE_TRAPEZOID )
+ {
+ 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() );
+
+ // Move aPad shape to relativePadPos
+ for( int ii = 0; ii < 4; ii++ )
+ polycompare[ii] += relativePadPos;
+
+ // And now test polygons:
+ if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) )
+ diag = false;
+ }
+ else
+ {
+ // Should not occur, because aPad and aRefPad are swapped
+ // to have only aPad shape RECT or TRAP and aRefPad shape TRAP or RECT.
+ wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad ref RECT @ %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_SHAPE_ROUNDRECT:
+ if( aPad->GetShape() == PAD_SHAPE_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_SHAPE_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_SHAPE_OVAL: /* an oval pad is like a track segment */
{
/* Create a track segment with same dimensions as the oval aRefPad
@@ -703,8 +979,11 @@
}
case PAD_SHAPE_TRAPEZOID:
- case PAD_SHAPE_RECT:
{
+ // at this point, aPad is also a trapezoid, because all other shapes
+ // have priority, and are already tested
+ wxASSERT( aPad->GetShape() == PAD_TRAPEZOID );
+
wxPoint polyref[4]; // Shape of aRefPad
wxPoint polycompare[4]; // Shape of aPad
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
@@ -738,6 +1017,7 @@
{
wxSize padHalfsize; // half dimension of the pad
wxPoint startPoint, endPoint;
+ int r;
int segmHalfWidth = aSegmentWidth / 2;
int distToLine = segmHalfWidth + aMinDist;
@@ -910,6 +1190,59 @@
return false;
break;
+
+ case PAD_SHAPE_ROUNDRECT:
+ 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;
+
+ 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;
+
+ r = aPad->GetRoundRectCornerRadius();
+
+ 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, distToLine + r, m_segmLength ) )
+ return false;
+
+ 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, distToLine + r, m_segmLength ) )
+ return false;
+
+ 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, distToLine + r, m_segmLength ) )
+ return false;
+
+ 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, distToLine + r, m_segmLength ) )
+ return false;
+
+ break;
case PAD_SHAPE_TRAPEZOID:
{
=== modified file 'pcbnew/exporters/gen_modules_placefile.cpp'
--- pcbnew/exporters/gen_modules_placefile.cpp 2015-11-17 16:18:00 +0000
+++ pcbnew/exporters/gen_modules_placefile.cpp 2016-01-12 18:41:34 +0000
@@ -723,7 +723,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-11-04 08:48:34 +0000
+++ pcbnew/kicad_plugin.cpp 2016-01-12 18:41:34 +0000
@@ -1229,6 +1229,7 @@
case PAD_SHAPE_RECT: shape = "rect"; break;
case PAD_SHAPE_OVAL: shape = "oval"; break;
case PAD_SHAPE_TRAPEZOID: shape = "trapezoid"; break;
+ case PAD_SHAPE_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-09-01 09:27:38 +0000
+++ pcbnew/pcb_painter.cpp 2016-01-12 18:41:34 +0000
@@ -653,6 +653,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_SHAPE_TRAPEZOID:
{
std::deque<VECTOR2D> pointList;
=== modified file 'pcbnew/pcb_parser.cpp'
--- pcbnew/pcb_parser.cpp 2015-11-17 16:18:00 +0000
+++ pcbnew/pcb_parser.cpp 2016-01-12 18:41:34 +0000
@@ -2197,8 +2197,12 @@
pad->SetShape( PAD_SHAPE_TRAPEZOID );
break;
+ case T_roundrect:
+ pad->SetShape( PAD_SHAPE_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-12-15 20:21:25 +0000
+++ pcbnew/plot_board_layers.cpp 2016-01-12 18:41:34 +0000
@@ -380,6 +380,7 @@
// Fall through:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_RECT:
+ case PAD_SHAPE_ROUNDRECT:
default:
itemplotter.PlotPad( pad, color, plotMode );
break;
=== modified file 'pcbnew/plot_brditems_plotter.cpp'
--- pcbnew/plot_brditems_plotter.cpp 2015-08-23 19:40:33 +0000
+++ pcbnew/plot_brditems_plotter.cpp 2016-01-12 18:41:34 +0000
@@ -91,6 +91,15 @@
}
break;
+ case PAD_SHAPE_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_SHAPE_RECT:
default:
m_plotter->FlashPadRect( shape_pos, aPad->GetSize(),
=== modified file 'pcbnew/router/pns_router.cpp'
--- pcbnew/router/pns_router.cpp 2015-11-18 14:35:17 +0000
+++ pcbnew/router/pns_router.cpp 2016-01-12 18:41:34 +0000
@@ -267,6 +267,11 @@
break;
}
+ case PAD_SHAPE_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;
@@ -339,6 +344,23 @@
{
shape->Append( wx_c + coords[ii] );
}
+
+ solid->SetShape( shape );
+ break;
+ }
+
+ case PAD_SHAPE_ROUNDRECT:
+ {
+ wxPoint coords[12];
+ int r = aPad->BuildPadPolygonRoundRect( coords, wxSize( 0, 0 ), aPad->GetOrientation() );
+
+ // FIXME: implement
+
+ SHAPE_CONVEX* shape = new SHAPE_CONVEX();
+ for( int ii = 0; ii < 12; ii++ )
+ {
+ shape->Append( wx_c + coords[ii] );
+ }
solid->SetShape( shape );
break;
Follow ups
References