diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 1de93f328b..63c6f2341a 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -552,7 +552,7 @@ void SHAPE_POLY_SET::InflateWithLinkedHoles( int aFactor, int aCircleSegmentsCou } -void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) +void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount, bool aPreseveCorners ) { // A static table to avoid repetitive calculations of the coefficient // 1.0 - cos( M_PI/aCircleSegmentsCount) @@ -562,10 +562,14 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) ClipperOffset c; + // N.B. using jtSquare here does not create square corners. They end up mitered by + // aFactor. Setting jtMiter and forcing the limit to be aFactor creates sharp corners. + JoinType type = aPreseveCorners ? jtMiter : jtRound; + for( const POLYGON& poly : m_polys ) { for( size_t i = 0; i < poly.size(); i++ ) - c.AddPath( poly[i].convertToClipper( i == 0 ), jtRound, etClosedPolygon ); + c.AddPath( poly[i].convertToClipper( i == 0 ), type, etClosedPolygon ); } PolyTree solution; @@ -591,6 +595,7 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) coeff = arc_tolerance_factor[aCircleSegmentsCount]; c.ArcTolerance = std::abs( aFactor ) * coeff; + c.MiterLimit = std::abs( aFactor ); c.Execute( solution, aFactor ); diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index 002b0b1f43..68b4d52ee0 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -830,14 +830,24 @@ class SHAPE_POLY_SET : public SHAPE void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ); - /** Performs outline inflation/deflation, using round corners. - * Polygons cna have holes, but not linked holes with main outlines, + /** + * Performs outline inflation/deflation, using (optionally) round corners. + * Polygons can have holes, but not linked holes with main outlines, * if aFactor < 0. * When aFactor is < 0 a bad shape can result from these extra-segments used to * link holes to main outlines * Use InflateWithLinkedHoles for these polygons, especially if aFactor < 0 + * + * @param aFactor - number of units to offset edges + * @param aCircleSegmentsCount - number of segments per 360° to use in curve approx + * @param aPreseveCorners - If true, use square joints to keep angles preserved */ - void Inflate( int aFactor, int aCircleSegmentsCount ); + void Inflate( int aFactor, int aCircleSegmentsCount, bool aPreseveCorners = false ); + + void Inflate( int aFactor, bool aPreseveCorners ) + { + Inflate( aFactor, 32, aPreseveCorners ); + } ///> Performs outline inflation/deflation, using round corners. ///> Polygons can have holes, and/or linked holes with main outlines. diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 516d369f9f..666bb3e9b1 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -1135,12 +1135,15 @@ ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode ) vertices.push_back( vertices[0] ); + SHAPE_POLY_SET polygon; + polygon.NewOutline(); + for( size_t i = 0; i < vertices.size() - 1; i++ ) { EVERTEX v1 = vertices[i]; // Append the corner - zone->AppendCorner( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ), -1 ); + polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) ); if( v1.curve ) { @@ -1164,14 +1167,22 @@ ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode ) fabs( a - end_angle ) > fabs( delta_angle ); a -= delta_angle ) { - zone->AppendCorner( - wxPoint( KiROUND( radius * cos( a ) ), - KiROUND( radius * sin( a ) ) ) + center, - -1 ); + polygon.Append( KiROUND( radius * cos( a ) ) + center.x, + KiROUND( radius * sin( a ) ) + center.y ); } } } + // Eagle traces the zone such that half of the pen width is outside the polygon. + // We trace the zone such that the copper is completely inside. + if( p.width.ToPcbUnits() > 0 ) + { + polygon.Inflate( p.width.ToPcbUnits() / 2, true ); + polygon.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + } + + zone->AddPolygon( polygon.COutline( 0 ) ); + // If the pour is a cutout it needs to be set to a keepout if( p.pour == EPOLYGON::CUTOUT ) { @@ -1187,13 +1198,13 @@ ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode ) zone->SetHatch( ZONE_CONTAINER::DIAGONAL_EDGE, zone->GetDefaultHatchPitch(), true ); // clearances, etc. - zone->SetArcSegmentCount( ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF ); // @todo: should be a constructor default? - zone->SetMinThickness( std::max( - ZONE_THICKNESS_MIN_VALUE_MIL*IU_PER_MILS, p.width.ToPcbUnits() ) ); + zone->SetArcSegmentCount( ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF ); + + // We divide the thickness by half because we are tracing _inside_ the zone outline + // This means the radius of curvature will be twice the size for an equivalent EAGLE zone + zone->SetMinThickness( + std::max( ZONE_THICKNESS_MIN_VALUE_MIL * IU_PER_MILS, p.width.ToPcbUnits() / 2 ) ); - // FIXME: KiCad zones have very rounded corners compared to eagle. - // This means that isolation amounts that work well in eagle - // tend to make copper intrude in soldermask free areas around pads. if( p.isolate ) zone->SetZoneClearance( p.isolate->ToPcbUnits() ); else @@ -1716,8 +1727,6 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const dwg->SetTimeStamp( EagleTimeStamp( aTree ) ); std::vector pts; - // TODO: I think there's no way to know a priori the number of children in wxXmlNode :() - // pts.reserve( aTree.size() ); // Get the first vertex and iterate wxXmlNode* vertex = aTree->GetChildren();